blob: a9ec566e6f8334a309efc37244fd4c4d95130236 [file] [log] [blame]
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +01001#!/usr/bin/env python3
Azim Khanf0e42fb2017-08-02 14:47:13 +01002# Test suites code generator.
3#
Mohammad Azim Khan78befd92018-03-06 11:49:41 +00004# Copyright (C) 2018, ARM Limited, All Rights Reserved
Azim Khanf0e42fb2017-08-02 14:47:13 +01005# SPDX-License-Identifier: Apache-2.0
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# This file is part of mbed TLS (https://tls.mbed.org)
20
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010021"""
Azim Khanaee05bb2018-07-02 16:01:04 +010022This script is a key part of Mbed TLS test suites framework. For
23understanding the script it is important to understand the
24framework. This doc string contains a summary of the framework
25and explains the function of this script.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010026
Azim Khanaee05bb2018-07-02 16:01:04 +010027Mbed TLS test suites:
28=====================
29Scope:
30------
31The test suites focus on unit testing the crypto primitives and also
32include x509 parser tests. Tests can be added to test any MBED TLS
33module. However, the framework is not capable of testing SSL
34protocol, since that requires full stack execution and that is best
35tested as part of the system test.
36
37Test case definition:
38---------------------
39Tests are defined in a test_suite_<module>[.<optional sub module>].data
40file. A test definition contains:
41 test name
42 optional build macro dependencies
43 test function
44 test parameters
45
46Test dependencies are build macros that can be specified to indicate
47the build config in which the test is valid. For example if a test
48depends on a feature that is only enabled by defining a macro. Then
49that macro should be specified as a dependency of the test.
50
51Test function is the function that implements the test steps. This
52function is specified for different tests that perform same steps
53with different parameters.
54
55Test parameters are specified in string form separated by ':'.
56Parameters can be of type string, binary data specified as hex
57string and integer constants specified as integer, macro or
58as an expression. Following is an example test definition:
59
60X509 CRL Unsupported critical extension (issuingDistributionPoint)
61depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA256_C
62mbedtls_x509_crl_parse:"data_files/crl-idp.pem":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG
63
64Test functions:
65---------------
66Test functions are coded in C in test_suite_<module>.function files.
67Functions file is itself not compilable and contains special
68format patterns to specify test suite dependencies, start and end
69of functions and function dependencies. Check any existing functions
70file for example.
71
72Execution:
73----------
74Tests are executed in 3 steps:
75- Generating test_suite_<module>[.<optional sub module>].c file
76 for each corresponding .data file.
77- Building each source file into executables.
78- Running each executable and printing report.
79
80Generating C test source requires more than just the test functions.
81Following extras are required:
82- Process main()
83- Reading .data file and dispatching test cases.
84- Platform specific test case execution
85- Dependency checking
86- Integer expression evaluation
87- Test function dispatch
88
89Build dependencies and integer expressions (in the test parameters)
90are specified as strings in the .data file. Their run time value is
91not known at the generation stage. Hence, they need to be translated
92into run time evaluations. This script generates the run time checks
93for dependencies and integer expressions.
94
95Similarly, function names have to be translated into function calls.
96This script also generates code for function dispatch.
97
98The extra code mentioned here is either generated by this script
99or it comes from the input files: helpers file, platform file and
100the template file.
101
102Helper file:
103------------
104Helpers file contains common helper/utility functions and data.
105
106Platform file:
107--------------
108Platform file contains platform specific setup code and test case
109dispatch code. For example, host_test.function reads test data
110file from host's file system and dispatches tests.
111In case of on-target target_test.function tests are not dispatched
112on target. Target code is kept minimum and only test functions are
113dispatched. Test case dispatch is done on the host using tools like
114Greentea.
115
116Template file:
117---------
118Template file for example main_test.function is a template C file in
119which generated code and code from input files is substituted to
120generate a compilable C file. It also contains skeleton functions for
121dependency checks, expression evaluation and function dispatch. These
122functions are populated with checks and return codes by this script.
123
124Template file contains "replacement" fields that are formatted
125strings processed by Python str.format() method.
126
127This script:
128============
129Core function of this script is to fill the template file with
130code that is generated or read from helpers and platform files.
131
132This script replaces following fields in the template and generates
133the test source file:
134
135{test_common_helpers} <-- All common code from helpers.function
136 is substituted here.
137{functions_code} <-- Test functions are substituted here
138 from the input test_suit_xyz.function
139 file. C preprocessor checks are generated
140 for the build dependencies specified
141 in the input file. This script also
142 generates wrappers for the test
143 functions with code to expand the
144 string parameters read from the data
145 file.
146{expression_code} <-- This script enumerates the
147 expressions in the .data file and
148 generates code to handle enumerated
149 expression Ids and return the values.
150{dep_check_code} <-- This script enumerates all
151 build dependencies and generate
152 code to handle enumerated build
153 dependency Id and return status: if
154 the dependency is defined or not.
155{dispatch_code} <-- This script enumerates the functions
156 specified in the input test data file
157 and generates the initializer for the
158 function table in the template
159 file.
160{platform_code} <-- Platform specific setup and test
161 dispatch code.
162
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100163"""
164
Azim Khanf0e42fb2017-08-02 14:47:13 +0100165
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100166import io
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100167import os
168import re
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100169import sys
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100170import argparse
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100171
172
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100173BEGIN_HEADER_REGEX = '/\*\s*BEGIN_HEADER\s*\*/'
174END_HEADER_REGEX = '/\*\s*END_HEADER\s*\*/'
175
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000176BEGIN_SUITE_HELPERS_REGEX = '/\*\s*BEGIN_SUITE_HELPERS\s*\*/'
177END_SUITE_HELPERS_REGEX = '/\*\s*END_SUITE_HELPERS\s*\*/'
178
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100179BEGIN_DEP_REGEX = 'BEGIN_DEPENDENCIES'
180END_DEP_REGEX = 'END_DEPENDENCIES'
181
182BEGIN_CASE_REGEX = '/\*\s*BEGIN_CASE\s*(.*?)\s*\*/'
183END_CASE_REGEX = '/\*\s*END_CASE\s*\*/'
184
185
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100186class GeneratorInputError(Exception):
187 """
Azim Khane3b26af2018-06-29 02:36:57 +0100188 Exception to indicate error in the input files to this script.
189 This includes missing patterns, test function names and other
190 parsing errors.
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100191 """
192 pass
193
194
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100195class FileWrapper(io.FileIO):
Azim Khan4b543232017-06-30 09:35:21 +0100196 """
Azim Khane3b26af2018-06-29 02:36:57 +0100197 This class extends built-in io.FileIO class with attribute line_no,
198 that indicates line number for the line that is read.
Azim Khan4b543232017-06-30 09:35:21 +0100199 """
200
201 def __init__(self, file_name):
202 """
Azim Khane3b26af2018-06-29 02:36:57 +0100203 Instantiate the base class and initialize the line number to 0.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100204
Azim Khanf0e42fb2017-08-02 14:47:13 +0100205 :param file_name: File path to open.
Azim Khan4b543232017-06-30 09:35:21 +0100206 """
207 super(FileWrapper, self).__init__(file_name, 'r')
208 self.line_no = 0
209
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100210 def __next__(self):
Azim Khan4b543232017-06-30 09:35:21 +0100211 """
Azim Khane3b26af2018-06-29 02:36:57 +0100212 Python 2 iterator method. This method overrides base class's
213 next method and extends the next method to count the line
214 numbers as each line is read.
215
216 It works for both Python 2 and Python 3 by checking iterator
217 method name in the base iterator object.
218
Azim Khanf0e42fb2017-08-02 14:47:13 +0100219 :return: Line read from file.
Azim Khan4b543232017-06-30 09:35:21 +0100220 """
Gilles Peskine667f7f82018-06-18 17:51:56 +0200221 parent = super(FileWrapper, self)
222 if hasattr(parent, '__next__'):
223 line = parent.__next__() # Python 3
224 else:
225 line = parent.next() # Python 2
Azim Khan4b543232017-06-30 09:35:21 +0100226 if line:
227 self.line_no += 1
Azim Khan936ea932018-06-28 16:47:12 +0100228 # Convert byte array to string with correct encoding and
229 # strip any whitespaces added in the decoding process.
230 return line.decode(sys.getdefaultencoding()).strip() + "\n"
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100231 return None
Azim Khane3b26af2018-06-29 02:36:57 +0100232
233 # Python 3 iterator method
Gilles Peskine667f7f82018-06-18 17:51:56 +0200234 next = __next__
Azim Khan4b543232017-06-30 09:35:21 +0100235
236
237def split_dep(dep):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100238 """
239 Split NOT character '!' from dependency. Used by gen_deps()
240
241 :param dep: Dependency list
Azim Khane3b26af2018-06-29 02:36:57 +0100242 :return: string tuple. Ex: ('!', MACRO) for !MACRO and ('', MACRO) for
243 MACRO.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100244 """
Azim Khan4b543232017-06-30 09:35:21 +0100245 return ('!', dep[1:]) if dep[0] == '!' else ('', dep)
246
247
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100248def gen_deps(deps):
249 """
Azim Khane3b26af2018-06-29 02:36:57 +0100250 Test suite data and functions specifies compile time dependencies.
251 This function generates C preprocessor code from the input
252 dependency list. Caller uses the generated preprocessor code to
253 wrap dependent code.
254 A dependency in the input list can have a leading '!' character
255 to negate a condition. '!' is separated from the dependency using
256 function split_dep() and proper preprocessor check is generated
257 accordingly.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100258
Azim Khanf0e42fb2017-08-02 14:47:13 +0100259 :param deps: List of dependencies.
Azim Khan040b6a22018-06-28 16:49:13 +0100260 :return: if defined and endif code with macro annotations for
261 readability.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100262 """
Azim Khan4b543232017-06-30 09:35:21 +0100263 dep_start = ''.join(['#if %sdefined(%s)\n' % split_dep(x) for x in deps])
264 dep_end = ''.join(['#endif /* %s */\n' % x for x in reversed(deps)])
265
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100266 return dep_start, dep_end
267
268
269def gen_deps_one_line(deps):
270 """
Azim Khane3b26af2018-06-29 02:36:57 +0100271 Similar to gen_deps() but generates dependency checks in one line.
272 Useful for generating code with #else block.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100273
Azim Khanf0e42fb2017-08-02 14:47:13 +0100274 :param deps: List of dependencies.
275 :return: ifdef code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100276 """
Azim Khan040b6a22018-06-28 16:49:13 +0100277 defines = '#if ' if len(deps) else ''
278 defines += ' && '.join(['%sdefined(%s)' % split_dep(x) for x in deps])
Azim Khan4b543232017-06-30 09:35:21 +0100279 return defines
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100280
281
Azim Khan4b543232017-06-30 09:35:21 +0100282def gen_function_wrapper(name, locals, args_dispatch):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100283 """
Azim Khan040b6a22018-06-28 16:49:13 +0100284 Creates test function wrapper code. A wrapper has the code to
285 unpack parameters from parameters[] array.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100286
Azim Khanf0e42fb2017-08-02 14:47:13 +0100287 :param name: Test function name
288 :param locals: Local variables declaration code
Azim Khan040b6a22018-06-28 16:49:13 +0100289 :param args_dispatch: List of dispatch arguments.
290 Ex: ['(char *)params[0]', '*((int *)params[1])']
Azim Khanf0e42fb2017-08-02 14:47:13 +0100291 :return: Test function wrapper.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100292 """
293 # Then create the wrapper
294 wrapper = '''
295void {name}_wrapper( void ** params )
296{{
Gilles Peskine77761412018-06-18 17:51:40 +0200297{unused_params}{locals}
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100298 {name}( {args} );
299}}
Gilles Peskine77761412018-06-18 17:51:40 +0200300'''.format(name=name,
Mohammad Azim Khanc3521df2018-06-26 14:06:52 +0100301 unused_params='' if args_dispatch else ' (void)params;\n',
Azim Khan4b543232017-06-30 09:35:21 +0100302 args=', '.join(args_dispatch),
303 locals=locals)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100304 return wrapper
305
306
307def gen_dispatch(name, deps):
308 """
Azim Khane3b26af2018-06-29 02:36:57 +0100309 Test suite code template main_test.function defines a C function
310 array to contain test case functions. This function generates an
311 initializer entry for a function in that array. The entry is
312 composed of a compile time check for the test function
313 dependencies. At compile time the test function is assigned when
314 dependencies are met, else NULL is assigned.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100315
Azim Khanf0e42fb2017-08-02 14:47:13 +0100316 :param name: Test function name
317 :param deps: List of dependencies
318 :return: Dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100319 """
320 if len(deps):
321 ifdef = gen_deps_one_line(deps)
322 dispatch_code = '''
323{ifdef}
324 {name}_wrapper,
325#else
326 NULL,
327#endif
328'''.format(ifdef=ifdef, name=name)
329 else:
330 dispatch_code = '''
331 {name}_wrapper,
332'''.format(name=name)
333
334 return dispatch_code
335
336
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000337def parse_until_pattern(funcs_f, end_regex):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100338 """
Azim Khane3b26af2018-06-29 02:36:57 +0100339 Matches pattern end_regex to the lines read from the file object.
340 Returns the lines read until end pattern is matched.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100341
Azim Khanf0e42fb2017-08-02 14:47:13 +0100342 :param funcs_f: file object for .functions file
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000343 :param end_regex: Pattern to stop parsing
Azim Khane3b26af2018-06-29 02:36:57 +0100344 :return: Lines read before the end pattern
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100345 """
Azim Khan4b543232017-06-30 09:35:21 +0100346 headers = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100347 for line in funcs_f:
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000348 if re.search(end_regex, line):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100349 break
350 headers += line
351 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100352 raise GeneratorInputError("file: %s - end pattern [%s] not found!" %
Azim Khan040b6a22018-06-28 16:49:13 +0100353 (funcs_f.name, end_regex))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100354
Azim Khan4b543232017-06-30 09:35:21 +0100355 return headers
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100356
357
Azim Khan4b543232017-06-30 09:35:21 +0100358def parse_suite_deps(funcs_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100359 """
Azim Khane3b26af2018-06-29 02:36:57 +0100360 Parses test suite dependencies specified at the top of a
361 .function file, that starts with pattern BEGIN_DEPENDENCIES
362 and end with END_DEPENDENCIES. Dependencies are specified
363 after pattern 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100364
Azim Khanf0e42fb2017-08-02 14:47:13 +0100365 :param funcs_f: file object for .functions file
366 :return: List of test suite dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100367 """
368 deps = []
369 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100370 m = re.search('depends_on\:(.*)', line.strip())
371 if m:
372 deps += [x.strip() for x in m.group(1).split(':')]
373 if re.search(END_DEP_REGEX, line):
374 break
375 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100376 raise GeneratorInputError("file: %s - end dependency pattern [%s]"
Azim Khan040b6a22018-06-28 16:49:13 +0100377 " not found!" % (funcs_f.name, END_DEP_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100378
Azim Khan4b543232017-06-30 09:35:21 +0100379 return deps
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100380
381
382def parse_function_deps(line):
383 """
Azim Khane3b26af2018-06-29 02:36:57 +0100384 Parses function dependencies, that are in the same line as
385 comment BEGIN_CASE. Dependencies are specified after pattern
386 'depends_on:' and are delimited by ':'.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100387
Azim Khanf0e42fb2017-08-02 14:47:13 +0100388 :param line: Line from .functions file that has dependencies.
389 :return: List of dependencies.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100390 """
391 deps = []
392 m = re.search(BEGIN_CASE_REGEX, line)
393 dep_str = m.group(1)
394 if len(dep_str):
395 m = re.search('depends_on:(.*)', dep_str)
396 if m:
Azim Khan4b543232017-06-30 09:35:21 +0100397 deps = [x.strip() for x in m.group(1).strip().split(':')]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100398 return deps
399
400
401def parse_function_signature(line):
402 """
Azim Khane3b26af2018-06-29 02:36:57 +0100403 Parses test function signature for validation and generates
404 a dispatch wrapper function that translates input test vectors
405 read from the data file into test function arguments.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100406
Azim Khan040b6a22018-06-28 16:49:13 +0100407 :param line: Line from .functions file that has a function
408 signature.
409 :return: function name, argument list, local variables for
410 wrapper function and argument dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100411 """
412 args = []
Azim Khan2397bba2017-06-09 04:35:03 +0100413 locals = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100414 args_dispatch = []
Azim Khane3b26af2018-06-29 02:36:57 +0100415 # Check if the test function returns void.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100416 m = re.search('\s*void\s+(\w+)\s*\(', line, re.I)
417 if not m:
418 raise ValueError("Test function should return 'void'\n%s" % line)
419 name = m.group(1)
420 line = line[len(m.group(0)):]
421 arg_idx = 0
422 for arg in line[:line.find(')')].split(','):
423 arg = arg.strip()
424 if arg == '':
425 continue
426 if re.search('int\s+.*', arg.strip()):
427 args.append('int')
428 args_dispatch.append('*( (int *) params[%d] )' % arg_idx)
429 elif re.search('char\s*\*\s*.*', arg.strip()):
430 args.append('char*')
431 args_dispatch.append('(char *) params[%d]' % arg_idx)
Azim Khan5fcca462018-06-29 11:05:32 +0100432 elif re.search('data_t\s*\*\s*.*', arg.strip()):
Azim Khana57a4202017-05-31 20:32:32 +0100433 args.append('hex')
Azim Khan2397bba2017-06-09 04:35:03 +0100434 # create a structure
Azim Khan040b6a22018-06-28 16:49:13 +0100435 pointer_initializer = '(uint8_t *) params[%d]' % arg_idx
436 len_initializer = '*( (uint32_t *) params[%d] )' % (arg_idx+1)
Azim Khan5fcca462018-06-29 11:05:32 +0100437 locals += """ data_t data%d = {%s, %s};
Azim Khan040b6a22018-06-28 16:49:13 +0100438""" % (arg_idx, pointer_initializer, len_initializer)
Azim Khan2397bba2017-06-09 04:35:03 +0100439
Azim Khan5fcca462018-06-29 11:05:32 +0100440 args_dispatch.append('&data%d' % arg_idx)
Azim Khan2397bba2017-06-09 04:35:03 +0100441 arg_idx += 1
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100442 else:
Azim Khan040b6a22018-06-28 16:49:13 +0100443 raise ValueError("Test function arguments can only be 'int', "
Azim Khan5fcca462018-06-29 11:05:32 +0100444 "'char *' or 'data_t'\n%s" % line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100445 arg_idx += 1
446
Azim Khan4b543232017-06-30 09:35:21 +0100447 return name, args, locals, args_dispatch
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100448
449
Azim Khan4b543232017-06-30 09:35:21 +0100450def parse_function_code(funcs_f, deps, suite_deps):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100451 """
Azim Khan040b6a22018-06-28 16:49:13 +0100452 Parses out a function from function file object and generates
453 function and dispatch code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100454
Azim Khanf0e42fb2017-08-02 14:47:13 +0100455 :param funcs_f: file object of the functions file.
456 :param deps: List of dependencies
457 :param suite_deps: List of test suite dependencies
458 :return: Function name, arguments, function code and dispatch code.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100459 """
Azim Khan4b543232017-06-30 09:35:21 +0100460 code = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100461 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100462 # Check function signature
463 m = re.match('.*?\s+(\w+)\s*\(', line, re.I)
464 if m:
465 # check if we have full signature i.e. split in more lines
466 if not re.match('.*\)', line):
467 for lin in funcs_f:
468 line += lin
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100469 if re.search('.*?\)', line):
470 break
Azim Khan4b543232017-06-30 09:35:21 +0100471 name, args, locals, args_dispatch = parse_function_signature(line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100472 code += line.replace(name, 'test_' + name)
473 name = 'test_' + name
474 break
475 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100476 raise GeneratorInputError("file: %s - Test functions not found!" %
Azim Khan040b6a22018-06-28 16:49:13 +0100477 funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100478
479 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100480 if re.search(END_CASE_REGEX, line):
481 break
482 code += line
483 else:
Azim Khane3b26af2018-06-29 02:36:57 +0100484 raise GeneratorInputError("file: %s - end case pattern [%s] not "
Azim Khan040b6a22018-06-28 16:49:13 +0100485 "found!" % (funcs_f.name, END_CASE_REGEX))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100486
487 # Add exit label if not present
488 if code.find('exit:') == -1:
489 s = code.rsplit('}', 1)
490 if len(s) == 2:
Azim Khan4b543232017-06-30 09:35:21 +0100491 code = """exit:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100492 ;;
Azim Khan4b543232017-06-30 09:35:21 +0100493}""".join(s)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100494
Azim Khan4b543232017-06-30 09:35:21 +0100495 code += gen_function_wrapper(name, locals, args_dispatch)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100496 ifdef, endif = gen_deps(deps)
497 dispatch_code = gen_dispatch(name, suite_deps + deps)
Azim Khan4b543232017-06-30 09:35:21 +0100498 return name, args, ifdef + code + endif, dispatch_code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100499
500
501def parse_functions(funcs_f):
502 """
Azim Khane3b26af2018-06-29 02:36:57 +0100503 Parses a test_suite_xxx.function file and returns information
504 for generating a C source file for the test suite.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100505
Azim Khanf0e42fb2017-08-02 14:47:13 +0100506 :param funcs_f: file object of the functions file.
Azim Khan040b6a22018-06-28 16:49:13 +0100507 :return: List of test suite dependencies, test function dispatch
508 code, function code and a dict with function identifiers
509 and arguments info.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100510 """
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100511 suite_headers = ''
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000512 suite_helpers = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100513 suite_deps = []
514 suite_functions = ''
515 func_info = {}
516 function_idx = 0
517 dispatch_code = ''
518 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100519 if re.search(BEGIN_HEADER_REGEX, line):
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000520 headers = parse_until_pattern(funcs_f, END_HEADER_REGEX)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100521 suite_headers += headers
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000522 elif re.search(BEGIN_SUITE_HELPERS_REGEX, line):
523 helpers = parse_until_pattern(funcs_f, END_SUITE_HELPERS_REGEX)
524 suite_helpers += helpers
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100525 elif re.search(BEGIN_DEP_REGEX, line):
Azim Khan4b543232017-06-30 09:35:21 +0100526 deps = parse_suite_deps(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100527 suite_deps += deps
528 elif re.search(BEGIN_CASE_REGEX, line):
529 deps = parse_function_deps(line)
Azim Khan040b6a22018-06-28 16:49:13 +0100530 func_name, args, func_code, func_dispatch =\
531 parse_function_code(funcs_f, deps, suite_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100532 suite_functions += func_code
533 # Generate dispatch code and enumeration info
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100534 if func_name in func_info:
535 raise GeneratorInputError(
536 "file: %s - function %s re-declared at line %d" % \
537 (funcs_f.name, func_name, funcs_f.line_no))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100538 func_info[func_name] = (function_idx, args)
539 dispatch_code += '/* Function Id: %d */\n' % function_idx
540 dispatch_code += func_dispatch
541 function_idx += 1
542
543 ifdef, endif = gen_deps(suite_deps)
Mohammad Azim Khanb5229292018-02-06 13:08:01 +0000544 func_code = ifdef + suite_headers + suite_helpers + suite_functions + endif
Azim Khan13c6bfb2017-06-15 14:45:56 +0100545 return suite_deps, dispatch_code, func_code, func_info
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100546
547
548def escaped_split(str, ch):
549 """
550 Split str on character ch but ignore escaped \{ch}
Azim Khan040b6a22018-06-28 16:49:13 +0100551 Since, return value is used to write back to the intermediate
552 data file, any escape characters in the input are retained in the
553 output.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100554
Azim Khanf0e42fb2017-08-02 14:47:13 +0100555 :param str: String to split
556 :param ch: split character
557 :return: List of splits
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100558 """
559 if len(ch) > 1:
560 raise ValueError('Expected split character. Found string!')
561 out = []
562 part = ''
563 escape = False
564 for i in range(len(str)):
565 if not escape and str[i] == ch:
566 out.append(part)
567 part = ''
568 else:
Azim Khanacc54732017-07-03 14:06:45 +0100569 part += str[i]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100570 escape = not escape and str[i] == '\\'
571 if len(part):
572 out.append(part)
573 return out
574
575
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100576def parse_test_data(data_f, debug=False):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100577 """
Azim Khane3b26af2018-06-29 02:36:57 +0100578 Parses .data file for each test case name, test function name,
579 test dependencies and test arguments. This information is
580 correlated with the test functions file for generating an
581 intermediate data file replacing the strings for test function
582 names, dependencies and integer constant expressions with
583 identifiers. Mainly for optimising space for on-target
584 execution.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100585
Azim Khanf0e42fb2017-08-02 14:47:13 +0100586 :param data_f: file object of the data file.
Azim Khan040b6a22018-06-28 16:49:13 +0100587 :return: Generator that yields test name, function name,
588 dependency list and function argument list.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100589 """
590 STATE_READ_NAME = 0
591 STATE_READ_ARGS = 1
592 state = STATE_READ_NAME
593 deps = []
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100594 name = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100595 for line in data_f:
596 line = line.strip()
597 if len(line) and line[0] == '#': # Skip comments
598 continue
599
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100600 # Blank line indicates end of test
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100601 if len(line) == 0:
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100602 if state == STATE_READ_ARGS:
Azim Khan040b6a22018-06-28 16:49:13 +0100603 raise GeneratorInputError("[%s:%d] Newline before arguments. "
604 "Test function and arguments "
605 "missing for %s" %
606 (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100607 continue
608
609 if state == STATE_READ_NAME:
610 # Read test name
611 name = line
612 state = STATE_READ_ARGS
613 elif state == STATE_READ_ARGS:
614 # Check dependencies
615 m = re.search('depends_on\:(.*)', line)
616 if m:
Azim Khan040b6a22018-06-28 16:49:13 +0100617 deps = [x.strip() for x in m.group(1).split(':') if len(
618 x.strip())]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100619 else:
620 # Read test vectors
621 parts = escaped_split(line, ':')
622 function = parts[0]
623 args = parts[1:]
624 yield name, function, deps, args
625 deps = []
626 state = STATE_READ_NAME
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100627 if state == STATE_READ_ARGS:
Azim Khan040b6a22018-06-28 16:49:13 +0100628 raise GeneratorInputError("[%s:%d] Newline before arguments. "
629 "Test function and arguments missing for "
630 "%s" % (data_f.name, data_f.line_no, name))
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100631
632
633def gen_dep_check(dep_id, dep):
634 """
Azim Khane3b26af2018-06-29 02:36:57 +0100635 Generate code for checking dependency with the associated
636 identifier.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100637
Azim Khanf0e42fb2017-08-02 14:47:13 +0100638 :param dep_id: Dependency identifier
639 :param dep: Dependency macro
640 :return: Dependency check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100641 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100642 if dep_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100643 raise GeneratorInputError("Dependency Id should be a positive "
644 "integer.")
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100645 noT, dep = ('!', dep[1:]) if dep[0] == '!' else ('', dep)
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100646 if len(dep) == 0:
647 raise GeneratorInputError("Dependency should not be an empty string.")
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100648 dep_check = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100649 case {id}:
650 {{
Azim Khand61b8372017-07-10 11:54:01 +0100651#if {noT}defined({macro})
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100652 ret = DEPENDENCY_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100653#else
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100654 ret = DEPENDENCY_NOT_SUPPORTED;
Azim Khand61b8372017-07-10 11:54:01 +0100655#endif
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100656 }}
657 break;'''.format(noT=noT, macro=dep, id=dep_id)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100658 return dep_check
659
660
661def gen_expression_check(exp_id, exp):
662 """
Azim Khane3b26af2018-06-29 02:36:57 +0100663 Generates code for evaluating an integer expression using
664 associated expression Id.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100665
Azim Khanf0e42fb2017-08-02 14:47:13 +0100666 :param exp_id: Expression Identifier
667 :param exp: Expression/Macro
668 :return: Expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100669 """
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100670 if exp_id < 0:
Azim Khan040b6a22018-06-28 16:49:13 +0100671 raise GeneratorInputError("Expression Id should be a positive "
672 "integer.")
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100673 if len(exp) == 0:
674 raise GeneratorInputError("Expression should not be an empty string.")
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100675 exp_code = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100676 case {exp_id}:
677 {{
678 *out_value = {expression};
679 }}
680 break;'''.format(exp_id=exp_id, expression=exp)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100681 return exp_code
682
683
Azim Khan599cd242017-07-06 17:34:27 +0100684def write_deps(out_data_f, test_deps, unique_deps):
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100685 """
Azim Khane3b26af2018-06-29 02:36:57 +0100686 Write dependencies to intermediate test data file, replacing
687 the string form with identifiers. Also, generates dependency
688 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100689
Azim Khanf0e42fb2017-08-02 14:47:13 +0100690 :param out_data_f: Output intermediate data file
691 :param test_deps: Dependencies
Azim Khan040b6a22018-06-28 16:49:13 +0100692 :param unique_deps: Mutable list to track unique dependencies
693 that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100694 :return: returns dependency check code.
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100695 """
Azim Khan599cd242017-07-06 17:34:27 +0100696 dep_check_code = ''
697 if len(test_deps):
698 out_data_f.write('depends_on')
699 for dep in test_deps:
700 if dep not in unique_deps:
701 unique_deps.append(dep)
702 dep_id = unique_deps.index(dep)
703 dep_check_code += gen_dep_check(dep_id, dep)
704 else:
705 dep_id = unique_deps.index(dep)
706 out_data_f.write(':' + str(dep_id))
707 out_data_f.write('\n')
708 return dep_check_code
709
710
711def write_parameters(out_data_f, test_args, func_args, unique_expressions):
712 """
Azim Khane3b26af2018-06-29 02:36:57 +0100713 Writes test parameters to the intermediate data file, replacing
714 the string form with identifiers. Also, generates expression
715 check code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100716
Azim Khanf0e42fb2017-08-02 14:47:13 +0100717 :param out_data_f: Output intermediate data file
718 :param test_args: Test parameters
719 :param func_args: Function arguments
Azim Khan040b6a22018-06-28 16:49:13 +0100720 :param unique_expressions: Mutable list to track unique
721 expressions that are global to this re-entrant function.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100722 :return: Returns expression check code.
Azim Khan599cd242017-07-06 17:34:27 +0100723 """
724 expression_code = ''
Mohammad Azim Khan1ec7e6f2018-04-11 23:46:37 +0100725 for i in range(len(test_args)):
Azim Khan599cd242017-07-06 17:34:27 +0100726 typ = func_args[i]
727 val = test_args[i]
728
Azim Khan040b6a22018-06-28 16:49:13 +0100729 # check if val is a non literal int val (i.e. an expression)
730 if typ == 'int' and not re.match('(\d+$)|((0x)?[0-9a-fA-F]+$)', val):
Azim Khan599cd242017-07-06 17:34:27 +0100731 typ = 'exp'
732 if val not in unique_expressions:
733 unique_expressions.append(val)
Azim Khan040b6a22018-06-28 16:49:13 +0100734 # exp_id can be derived from len(). But for
735 # readability and consistency with case of existing
736 # let's use index().
Azim Khan599cd242017-07-06 17:34:27 +0100737 exp_id = unique_expressions.index(val)
738 expression_code += gen_expression_check(exp_id, val)
739 val = exp_id
740 else:
741 val = unique_expressions.index(val)
742 out_data_f.write(':' + typ + ':' + str(val))
743 out_data_f.write('\n')
744 return expression_code
745
746
747def gen_suite_deps_checks(suite_deps, dep_check_code, expression_code):
748 """
Azim Khane3b26af2018-06-29 02:36:57 +0100749 Generates preprocessor checks for test suite dependencies.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100750
Azim Khan040b6a22018-06-28 16:49:13 +0100751 :param suite_deps: Test suite dependencies read from the
752 .functions file.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100753 :param dep_check_code: Dependency check code
754 :param expression_code: Expression check code
Azim Khan040b6a22018-06-28 16:49:13 +0100755 :return: Dependency and expression code guarded by test suite
756 dependencies.
Azim Khan599cd242017-07-06 17:34:27 +0100757 """
Azim Khan599cd242017-07-06 17:34:27 +0100758 if len(suite_deps):
759 ifdef = gen_deps_one_line(suite_deps)
760 dep_check_code = '''
761{ifdef}
762{code}
Azim Khan599cd242017-07-06 17:34:27 +0100763#endif
764'''.format(ifdef=ifdef, code=dep_check_code)
765 expression_code = '''
766{ifdef}
767{code}
Azim Khan599cd242017-07-06 17:34:27 +0100768#endif
769'''.format(ifdef=ifdef, code=expression_code)
770 return dep_check_code, expression_code
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100771
772
Azim Khan13c6bfb2017-06-15 14:45:56 +0100773def gen_from_test_data(data_f, out_data_f, func_info, suite_deps):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100774 """
Azim Khane3b26af2018-06-29 02:36:57 +0100775 This function reads test case name, dependencies and test vectors
776 from the .data file. This information is correlated with the test
777 functions file for generating an intermediate data file replacing
778 the strings for test function names, dependencies and integer
779 constant expressions with identifiers. Mainly for optimising
780 space for on-target execution.
781 It also generates test case dependency check code and expression
782 evaluation code.
Mohammad Azim Khanb73159d2018-06-13 16:31:26 +0100783
Azim Khanf0e42fb2017-08-02 14:47:13 +0100784 :param data_f: Data file object
785 :param out_data_f:Output intermediate data file
Azim Khan040b6a22018-06-28 16:49:13 +0100786 :param func_info: Dict keyed by function and with function id
787 and arguments info
Azim Khanf0e42fb2017-08-02 14:47:13 +0100788 :param suite_deps: Test suite deps
789 :return: Returns dependency and expression check code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100790 """
791 unique_deps = []
792 unique_expressions = []
793 dep_check_code = ''
794 expression_code = ''
Azim Khan040b6a22018-06-28 16:49:13 +0100795 for test_name, function_name, test_deps, test_args in parse_test_data(
796 data_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100797 out_data_f.write(test_name + '\n')
798
Azim Khan599cd242017-07-06 17:34:27 +0100799 # Write deps
800 dep_check_code += write_deps(out_data_f, test_deps, unique_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100801
Azim Khan599cd242017-07-06 17:34:27 +0100802 # Write test function name
803 test_function_name = 'test_' + function_name
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100804 if test_function_name not in func_info:
Azim Khan040b6a22018-06-28 16:49:13 +0100805 raise GeneratorInputError("Function %s not found!" %
806 test_function_name)
Azim Khan599cd242017-07-06 17:34:27 +0100807 func_id, func_args = func_info[test_function_name]
808 out_data_f.write(str(func_id))
809
810 # Write parameters
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100811 if len(test_args) != len(func_args):
Azim Khan040b6a22018-06-28 16:49:13 +0100812 raise GeneratorInputError("Invalid number of arguments in test "
813 "%s. See function %s signature." % (
814 test_name, function_name))
815 expression_code += write_parameters(out_data_f, test_args, func_args,
816 unique_expressions)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100817
Azim Khan599cd242017-07-06 17:34:27 +0100818 # Write a newline as test case separator
819 out_data_f.write('\n')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100820
Azim Khan040b6a22018-06-28 16:49:13 +0100821 dep_check_code, expression_code = gen_suite_deps_checks(
822 suite_deps, dep_check_code, expression_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100823 return dep_check_code, expression_code
824
825
Azim Khan040b6a22018-06-28 16:49:13 +0100826def generate_code(funcs_file, data_file, template_file, platform_file,
Azim Khane3b26af2018-06-29 02:36:57 +0100827 helpers_file, suites_dir, c_file, out_data_file):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100828 """
Azim Khane3b26af2018-06-29 02:36:57 +0100829 Generates C source code from test suite file, data file, common
830 helpers file and platform file.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100831
Azim Khanf0e42fb2017-08-02 14:47:13 +0100832 :param funcs_file: Functions file object
833 :param data_file: Data file object
834 :param template_file: Template file object
835 :param platform_file: Platform file object
Azim Khane3b26af2018-06-29 02:36:57 +0100836 :param helpers_file: Helper functions file object
Azim Khanf0e42fb2017-08-02 14:47:13 +0100837 :param suites_dir: Test suites dir
838 :param c_file: Output C file object
839 :param out_data_file: Output intermediate data file object
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100840 :return:
841 """
842 for name, path in [('Functions file', funcs_file),
843 ('Data file', data_file),
844 ('Template file', template_file),
845 ('Platform file', platform_file),
Azim Khane3b26af2018-06-29 02:36:57 +0100846 ('Helpers code file', helpers_file),
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100847 ('Suites dir', suites_dir)]:
848 if not os.path.exists(path):
849 raise IOError("ERROR: %s [%s] not found!" % (name, path))
850
851 snippets = {'generator_script' : os.path.basename(__file__)}
852
853 # Read helpers
Azim Khane3b26af2018-06-29 02:36:57 +0100854 with open(helpers_file, 'r') as help_f, open(platform_file, 'r') as \
Azim Khan040b6a22018-06-28 16:49:13 +0100855 platform_f:
Azim Khane3b26af2018-06-29 02:36:57 +0100856 snippets['test_common_helper_file'] = helpers_file
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100857 snippets['test_common_helpers'] = help_f.read()
858 snippets['test_platform_file'] = platform_file
Azim Khan040b6a22018-06-28 16:49:13 +0100859 snippets['platform_code'] = platform_f.read().replace(
860 'DATA_FILE', out_data_file.replace('\\', '\\\\')) # escape '\'
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100861
862 # Function code
Azim Khan040b6a22018-06-28 16:49:13 +0100863 with FileWrapper(funcs_file) as funcs_f, FileWrapper(data_file) as \
864 data_f, open(out_data_file, 'w') as out_data_f:
865 suite_deps, dispatch_code, func_code, func_info = parse_functions(
866 funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100867 snippets['functions_code'] = func_code
868 snippets['dispatch_code'] = dispatch_code
Azim Khan040b6a22018-06-28 16:49:13 +0100869 dep_check_code, expression_code = gen_from_test_data(
870 data_f, out_data_f, func_info, suite_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100871 snippets['dep_check_code'] = dep_check_code
872 snippets['expression_code'] = expression_code
873
874 snippets['test_file'] = c_file
875 snippets['test_main_file'] = template_file
876 snippets['test_case_file'] = funcs_file
877 snippets['test_case_data_file'] = data_file
878 # Read Template
879 # Add functions
880 #
881 with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
882 line_no = 1
883 for line in template_f.readlines():
Azim Khan040b6a22018-06-28 16:49:13 +0100884 # Update line number. +1 as #line directive sets next line number
885 snippets['line_no'] = line_no + 1
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100886 code = line.format(**snippets)
887 c_f.write(code)
888 line_no += 1
889
890
891def check_cmd():
892 """
893 Command line parser.
894
895 :return:
896 """
Azim Khan040b6a22018-06-28 16:49:13 +0100897 parser = argparse.ArgumentParser(
Azim Khane3b26af2018-06-29 02:36:57 +0100898 description='Dynamically generate test suite code.')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100899
900 parser.add_argument("-f", "--functions-file",
901 dest="funcs_file",
902 help="Functions file",
Azim Khane3b26af2018-06-29 02:36:57 +0100903 metavar="FUNCTIONS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100904 required=True)
905
906 parser.add_argument("-d", "--data-file",
907 dest="data_file",
908 help="Data file",
Azim Khane3b26af2018-06-29 02:36:57 +0100909 metavar="DATA_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100910 required=True)
911
912 parser.add_argument("-t", "--template-file",
913 dest="template_file",
914 help="Template file",
Azim Khane3b26af2018-06-29 02:36:57 +0100915 metavar="TEMPLATE_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100916 required=True)
917
918 parser.add_argument("-s", "--suites-dir",
919 dest="suites_dir",
920 help="Suites dir",
Azim Khane3b26af2018-06-29 02:36:57 +0100921 metavar="SUITES_DIR",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100922 required=True)
923
Azim Khane3b26af2018-06-29 02:36:57 +0100924 parser.add_argument("--helpers-file",
925 dest="helpers_file",
926 help="Helpers file",
927 metavar="HELPERS_FILE",
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100928 required=True)
929
930 parser.add_argument("-p", "--platform-file",
931 dest="platform_file",
932 help="Platform code file",
933 metavar="PLATFORM_FILE",
934 required=True)
935
936 parser.add_argument("-o", "--out-dir",
937 dest="out_dir",
938 help="Dir where generated code and scripts are copied",
939 metavar="OUT_DIR",
940 required=True)
941
942 args = parser.parse_args()
943
944 data_file_name = os.path.basename(args.data_file)
945 data_name = os.path.splitext(data_file_name)[0]
946
947 out_c_file = os.path.join(args.out_dir, data_name + '.c')
Mohammad Azim Khan00c4b092018-06-28 13:10:19 +0100948 out_data_file = os.path.join(args.out_dir, data_name + '.datax')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100949
950 out_c_file_dir = os.path.dirname(out_c_file)
951 out_data_file_dir = os.path.dirname(out_data_file)
952 for d in [out_c_file_dir, out_data_file_dir]:
953 if not os.path.exists(d):
954 os.makedirs(d)
955
Azim Khan040b6a22018-06-28 16:49:13 +0100956 generate_code(args.funcs_file, args.data_file, args.template_file,
Azim Khane3b26af2018-06-29 02:36:57 +0100957 args.platform_file, args.helpers_file, args.suites_dir,
Azim Khan040b6a22018-06-28 16:49:13 +0100958 out_c_file, out_data_file)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100959
960
961if __name__ == "__main__":
Mohammad Azim Khan3b06f222018-06-26 14:35:25 +0100962 try:
963 check_cmd()
964 except GeneratorInputError as e:
965 script_name = os.path.basename(sys.argv[0])
966 print("%s: input error: %s" % (script_name, str(e)))