blob: 7af6fdf29bc1dbac443ed5712e4ce0057e6fb25a [file] [log] [blame]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01001"""
Azim Khan4b543232017-06-30 09:35:21 +01002mbed TLS
3Copyright (c) 2017 ARM Limited
Mohammad Azim Khanfff49042017-03-28 01:48:31 +01004
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16"""
17
18import os
19import re
20import argparse
21import shutil
22
23
24"""
25Generates code in following structure.
26
27<output dir>/
Azim Khan4b543232017-06-30 09:35:21 +010028 |-- mbedtls/
29 | |-- <test suite #1>/
30 | | |-- main.c
31 | | |-- *.data files
32 | ...
33 | |-- <test suite #n>/
34 | | |-- main.c
35 | | |-- *.data files
36 | |
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010037"""
38
39
40BEGIN_HEADER_REGEX = '/\*\s*BEGIN_HEADER\s*\*/'
41END_HEADER_REGEX = '/\*\s*END_HEADER\s*\*/'
42
43BEGIN_DEP_REGEX = 'BEGIN_DEPENDENCIES'
44END_DEP_REGEX = 'END_DEPENDENCIES'
45
46BEGIN_CASE_REGEX = '/\*\s*BEGIN_CASE\s*(.*?)\s*\*/'
47END_CASE_REGEX = '/\*\s*END_CASE\s*\*/'
48
49
50class InvalidFileFormat(Exception):
51 """
52 Exception to indicate invalid file format.
53 """
54 pass
55
56
Azim Khan4b543232017-06-30 09:35:21 +010057class FileWrapper(file):
58 """
59 File wrapper class. Provides reading with line no. tracking.
60 """
61
62 def __init__(self, file_name):
63 """
64 Init file handle.
65
66 :param file_name:
67 """
68 super(FileWrapper, self).__init__(file_name, 'r')
69 self.line_no = 0
70
71 def next(self):
72 """
73 Iterator return impl.
74 :return:
75 """
76 line = super(FileWrapper, self).next()
77 if line:
78 self.line_no += 1
79 return line
80
81 def readline(self, limit=0):
82 """
83 Wrap the base class readline.
84
85 :param limit:
86 :return:
87 """
88 return self.next()
89
90
91def split_dep(dep):
92 return ('!', dep[1:]) if dep[0] == '!' else ('', dep)
93
94
Mohammad Azim Khanfff49042017-03-28 01:48:31 +010095def gen_deps(deps):
96 """
97 Generates dependency i.e. if def and endif code
98
99 :param deps:
100 :return:
101 """
Azim Khan4b543232017-06-30 09:35:21 +0100102 dep_start = ''.join(['#if %sdefined(%s)\n' % split_dep(x) for x in deps])
103 dep_end = ''.join(['#endif /* %s */\n' % x for x in reversed(deps)])
104
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100105 return dep_start, dep_end
106
107
108def gen_deps_one_line(deps):
109 """
110 Generates dependency checks in one line. Useful for writing code in #else case.
111
112 :param deps:
113 :return:
114 """
Azim Khan4b543232017-06-30 09:35:21 +0100115 defines = ('#if ' if len(deps) else '') + ' && '.join(['%sdefined(%s)' % split_dep(x) for x in deps])
116 return defines
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100117
118
Azim Khan4b543232017-06-30 09:35:21 +0100119def gen_function_wrapper(name, locals, args_dispatch):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100120 """
121 Creates test function code
122
123 :param name:
Azim Khan4b543232017-06-30 09:35:21 +0100124 :param locals:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100125 :param args_dispatch:
126 :return:
127 """
128 # Then create the wrapper
129 wrapper = '''
130void {name}_wrapper( void ** params )
131{{
132 {unused_params}
Azim Khan2397bba2017-06-09 04:35:03 +0100133{locals}
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100134 {name}( {args} );
135}}
Azim Khan4b543232017-06-30 09:35:21 +0100136'''.format(name=name, unused_params='(void)params;' if len(args_dispatch) == 0 else '',
137 args=', '.join(args_dispatch),
138 locals=locals)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100139 return wrapper
140
141
142def gen_dispatch(name, deps):
143 """
144 Generates dispatch condition for the functions.
145
146 :param name:
147 :param deps:
148 :return:
149 """
150 if len(deps):
151 ifdef = gen_deps_one_line(deps)
152 dispatch_code = '''
153{ifdef}
154 {name}_wrapper,
155#else
156 NULL,
157#endif
158'''.format(ifdef=ifdef, name=name)
159 else:
160 dispatch_code = '''
161 {name}_wrapper,
162'''.format(name=name)
163
164 return dispatch_code
165
166
Azim Khan4b543232017-06-30 09:35:21 +0100167def parse_suite_headers(funcs_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100168 """
169 Parses function headers.
170
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100171 :param funcs_f:
172 :return:
173 """
Azim Khan4b543232017-06-30 09:35:21 +0100174 headers = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100175 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100176 if re.search(END_HEADER_REGEX, line):
177 break
178 headers += line
179 else:
180 raise InvalidFileFormat("file: %s - end header pattern [%s] not found!" % (funcs_f.name, END_HEADER_REGEX))
181
Azim Khan4b543232017-06-30 09:35:21 +0100182 return headers
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100183
184
Azim Khan4b543232017-06-30 09:35:21 +0100185def parse_suite_deps(funcs_f):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100186 """
187 Parses function dependencies.
188
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100189 :param funcs_f:
190 :return:
191 """
192 deps = []
193 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100194 m = re.search('depends_on\:(.*)', line.strip())
195 if m:
196 deps += [x.strip() for x in m.group(1).split(':')]
197 if re.search(END_DEP_REGEX, line):
198 break
199 else:
200 raise InvalidFileFormat("file: %s - end dependency pattern [%s] not found!" % (funcs_f.name, END_DEP_REGEX))
201
Azim Khan4b543232017-06-30 09:35:21 +0100202 return deps
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100203
204
205def parse_function_deps(line):
206 """
207
208 :param line:
209 :return:
210 """
211 deps = []
212 m = re.search(BEGIN_CASE_REGEX, line)
213 dep_str = m.group(1)
214 if len(dep_str):
215 m = re.search('depends_on:(.*)', dep_str)
216 if m:
Azim Khan4b543232017-06-30 09:35:21 +0100217 deps = [x.strip() for x in m.group(1).strip().split(':')]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100218 return deps
219
220
221def parse_function_signature(line):
222 """
223 Parsing function signature
224
225 :param line:
226 :return:
227 """
228 args = []
Azim Khan2397bba2017-06-09 04:35:03 +0100229 locals = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100230 args_dispatch = []
231 m = re.search('\s*void\s+(\w+)\s*\(', line, re.I)
232 if not m:
233 raise ValueError("Test function should return 'void'\n%s" % line)
234 name = m.group(1)
235 line = line[len(m.group(0)):]
236 arg_idx = 0
237 for arg in line[:line.find(')')].split(','):
238 arg = arg.strip()
239 if arg == '':
240 continue
241 if re.search('int\s+.*', arg.strip()):
242 args.append('int')
243 args_dispatch.append('*( (int *) params[%d] )' % arg_idx)
244 elif re.search('char\s*\*\s*.*', arg.strip()):
245 args.append('char*')
246 args_dispatch.append('(char *) params[%d]' % arg_idx)
Azim Khan2397bba2017-06-09 04:35:03 +0100247 elif re.search('HexParam_t\s*\*\s*.*', arg.strip()):
Azim Khana57a4202017-05-31 20:32:32 +0100248 args.append('hex')
Azim Khan2397bba2017-06-09 04:35:03 +0100249 # create a structure
250 locals += """ HexParam_t hex%d = {%s, %s};
251""" % (arg_idx, '(uint8_t *) params[%d]' % arg_idx, '*( (uint32_t *) params[%d] )' % (arg_idx + 1))
252
253 args_dispatch.append('&hex%d' % arg_idx)
254 arg_idx += 1
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100255 else:
Azim Khan4b543232017-06-30 09:35:21 +0100256 raise ValueError("Test function arguments can only be 'int', 'char *' or 'HexParam_t'\n%s" % line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100257 arg_idx += 1
258
Azim Khan4b543232017-06-30 09:35:21 +0100259 return name, args, locals, args_dispatch
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100260
261
Azim Khan4b543232017-06-30 09:35:21 +0100262def parse_function_code(funcs_f, deps, suite_deps):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100263 """
264
265 :param line_no:
266 :param funcs_f:
267 :param deps:
268 :param suite_deps:
269 :return:
270 """
Azim Khan4b543232017-06-30 09:35:21 +0100271 code = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100272 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100273 # Check function signature
274 m = re.match('.*?\s+(\w+)\s*\(', line, re.I)
275 if m:
276 # check if we have full signature i.e. split in more lines
277 if not re.match('.*\)', line):
278 for lin in funcs_f:
279 line += lin
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100280 if re.search('.*?\)', line):
281 break
Azim Khan4b543232017-06-30 09:35:21 +0100282 name, args, locals, args_dispatch = parse_function_signature(line)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100283 code += line.replace(name, 'test_' + name)
284 name = 'test_' + name
285 break
286 else:
287 raise InvalidFileFormat("file: %s - Test functions not found!" % funcs_f.name)
288
289 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100290 if re.search(END_CASE_REGEX, line):
291 break
292 code += line
293 else:
294 raise InvalidFileFormat("file: %s - end case pattern [%s] not found!" % (funcs_f.name, END_CASE_REGEX))
295
296 # Add exit label if not present
297 if code.find('exit:') == -1:
298 s = code.rsplit('}', 1)
299 if len(s) == 2:
Azim Khan4b543232017-06-30 09:35:21 +0100300 code = """exit:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100301 ;;
Azim Khan4b543232017-06-30 09:35:21 +0100302}""".join(s)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100303
Azim Khan4b543232017-06-30 09:35:21 +0100304 code += gen_function_wrapper(name, locals, args_dispatch)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100305 ifdef, endif = gen_deps(deps)
306 dispatch_code = gen_dispatch(name, suite_deps + deps)
Azim Khan4b543232017-06-30 09:35:21 +0100307 return name, args, ifdef + code + endif, dispatch_code
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100308
309
310def parse_functions(funcs_f):
311 """
312 Returns functions code pieces
313
314 :param funcs_f:
315 :return:
316 """
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100317 suite_headers = ''
318 suite_deps = []
319 suite_functions = ''
320 func_info = {}
321 function_idx = 0
322 dispatch_code = ''
323 for line in funcs_f:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100324 if re.search(BEGIN_HEADER_REGEX, line):
Azim Khan4b543232017-06-30 09:35:21 +0100325 headers = parse_suite_headers(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100326 suite_headers += headers
327 elif re.search(BEGIN_DEP_REGEX, line):
Azim Khan4b543232017-06-30 09:35:21 +0100328 deps = parse_suite_deps(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100329 suite_deps += deps
330 elif re.search(BEGIN_CASE_REGEX, line):
331 deps = parse_function_deps(line)
Azim Khan4b543232017-06-30 09:35:21 +0100332 func_name, args, func_code, func_dispatch = parse_function_code(funcs_f, deps, suite_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100333 suite_functions += func_code
334 # Generate dispatch code and enumeration info
335 assert func_name not in func_info, "file: %s - function %s re-declared at line %d" % \
Azim Khan4b543232017-06-30 09:35:21 +0100336 (funcs_f.name, func_name, funcs_f.line_no)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100337 func_info[func_name] = (function_idx, args)
338 dispatch_code += '/* Function Id: %d */\n' % function_idx
339 dispatch_code += func_dispatch
340 function_idx += 1
341
342 ifdef, endif = gen_deps(suite_deps)
Azim Khan13c6bfb2017-06-15 14:45:56 +0100343 func_code = ifdef + suite_headers + suite_functions + endif
344 return suite_deps, dispatch_code, func_code, func_info
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100345
346
347def escaped_split(str, ch):
348 """
349 Split str on character ch but ignore escaped \{ch}
Azim Khan599cd242017-07-06 17:34:27 +0100350 Since return value is used to write back to the intermediate data file.
351 Any escape characters in the input are retained in the output.
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100352
353 :param str:
354 :param ch:
355 :return:
356 """
357 if len(ch) > 1:
358 raise ValueError('Expected split character. Found string!')
359 out = []
360 part = ''
361 escape = False
362 for i in range(len(str)):
363 if not escape and str[i] == ch:
364 out.append(part)
365 part = ''
366 else:
Azim Khanacc54732017-07-03 14:06:45 +0100367 part += str[i]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100368 escape = not escape and str[i] == '\\'
369 if len(part):
370 out.append(part)
371 return out
372
373
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100374def parse_test_data(data_f, debug=False):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100375 """
376 Parses .data file
377
378 :param data_f:
379 :return:
380 """
381 STATE_READ_NAME = 0
382 STATE_READ_ARGS = 1
383 state = STATE_READ_NAME
384 deps = []
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100385 name = ''
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100386 for line in data_f:
387 line = line.strip()
388 if len(line) and line[0] == '#': # Skip comments
389 continue
390
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100391 # Blank line indicates end of test
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100392 if len(line) == 0:
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100393 assert state != STATE_READ_ARGS, "Newline before arguments. " \
394 "Test function and arguments missing for %s" % name
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100395 continue
396
397 if state == STATE_READ_NAME:
398 # Read test name
399 name = line
400 state = STATE_READ_ARGS
401 elif state == STATE_READ_ARGS:
402 # Check dependencies
403 m = re.search('depends_on\:(.*)', line)
404 if m:
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100405 deps = [x.strip() for x in m.group(1).split(':') if len(x.strip())]
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100406 else:
407 # Read test vectors
408 parts = escaped_split(line, ':')
409 function = parts[0]
410 args = parts[1:]
411 yield name, function, deps, args
412 deps = []
413 state = STATE_READ_NAME
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100414 assert state != STATE_READ_ARGS, "Newline before arguments. " \
415 "Test function and arguments missing for %s" % name
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100416
417
418def gen_dep_check(dep_id, dep):
419 """
420 Generate code for the dependency.
421
422 :param dep_id:
423 :param dep:
424 :return:
425 """
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100426 assert dep_id > -1, "Dependency Id should be a positive integer."
427 noT, dep = ('!', dep[1:]) if dep[0] == '!' else ('', dep)
428 assert len(dep) > 0, "Dependency should not be an empty string."
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100429 dep_check = '''
430if ( dep_id == {id} )
431{{
432#if {noT}defined({macro})
433 return( DEPENDENCY_SUPPORTED );
434#else
435 return( DEPENDENCY_NOT_SUPPORTED );
436#endif
437}}
438else
439'''.format(noT=noT, macro=dep, id=dep_id)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100440 return dep_check
441
442
443def gen_expression_check(exp_id, exp):
444 """
445 Generates code for expression check
446
447 :param exp_id:
448 :param exp:
449 :return:
450 """
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100451 assert exp_id > -1, "Expression Id should be a positive integer."
452 assert len(exp) > 0, "Expression should not be an empty string."
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100453 exp_code = '''
454if ( exp_id == {exp_id} )
455{{
456 *out_value = {expression};
457}}
458else
459'''.format(exp_id=exp_id, expression=exp)
460 return exp_code
461
462
Azim Khan599cd242017-07-06 17:34:27 +0100463def write_deps(out_data_f, test_deps, unique_deps):
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100464 """
Azim Khan599cd242017-07-06 17:34:27 +0100465 Write dependencies to intermediate test data file.
466 It also returns dependency check code.
467
468 :param out_data_f:
469 :param dep:
470 :param unique_deps:
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100471 :return:
472 """
Azim Khan599cd242017-07-06 17:34:27 +0100473 dep_check_code = ''
474 if len(test_deps):
475 out_data_f.write('depends_on')
476 for dep in test_deps:
477 if dep not in unique_deps:
478 unique_deps.append(dep)
479 dep_id = unique_deps.index(dep)
480 dep_check_code += gen_dep_check(dep_id, dep)
481 else:
482 dep_id = unique_deps.index(dep)
483 out_data_f.write(':' + str(dep_id))
484 out_data_f.write('\n')
485 return dep_check_code
486
487
488def write_parameters(out_data_f, test_args, func_args, unique_expressions):
489 """
490 Writes test parameters to the intermediate data file.
491 Also generates expression code.
492
493 :param out_data_f:
494 :param test_args:
495 :param func_args:
496 :param unique_expressions:
497 :return:
498 """
499 expression_code = ''
500 for i in xrange(len(test_args)):
501 typ = func_args[i]
502 val = test_args[i]
503
504 # check if val is a non literal int val
505 if typ == 'int' and not re.match('(\d+$)|((0x)?[0-9a-fA-F]+$)', val): # its an expression
506 typ = 'exp'
507 if val not in unique_expressions:
508 unique_expressions.append(val)
509 # exp_id can be derived from len(). But for readability and consistency with case of existing let's
510 # use index().
511 exp_id = unique_expressions.index(val)
512 expression_code += gen_expression_check(exp_id, val)
513 val = exp_id
514 else:
515 val = unique_expressions.index(val)
516 out_data_f.write(':' + typ + ':' + str(val))
517 out_data_f.write('\n')
518 return expression_code
519
520
521def gen_suite_deps_checks(suite_deps, dep_check_code, expression_code):
522 """
523 Adds preprocessor checks for test suite dependencies.
524
525 :param suite_deps:
526 :param dep_check_code:
527 :param expression_code:
528 :return:
529 """
530 # void unused params
531 if len(dep_check_code) == 0:
532 dep_check_code = '(void) dep_id;\n'
533 if len(expression_code) == 0:
534 expression_code = '(void) exp_id;\n'
535 expression_code += '(void) out_value;\n'
536
537 if len(suite_deps):
538 ifdef = gen_deps_one_line(suite_deps)
539 dep_check_code = '''
540{ifdef}
541{code}
542#else
543(void) dep_id;
544#endif
545'''.format(ifdef=ifdef, code=dep_check_code)
546 expression_code = '''
547{ifdef}
548{code}
549#else
550(void) exp_id;
551(void) out_value;
552#endif
553'''.format(ifdef=ifdef, code=expression_code)
554 return dep_check_code, expression_code
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100555
556
Azim Khan13c6bfb2017-06-15 14:45:56 +0100557def gen_from_test_data(data_f, out_data_f, func_info, suite_deps):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100558 """
559 Generates dependency checks, expression code and intermediate data file from test data file.
560
561 :param data_f:
562 :param out_data_f:
563 :param func_info:
Azim Khan13c6bfb2017-06-15 14:45:56 +0100564 :param suite_deps:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100565 :return:
566 """
567 unique_deps = []
568 unique_expressions = []
569 dep_check_code = ''
570 expression_code = ''
571 for test_name, function_name, test_deps, test_args in parse_test_data(data_f):
572 out_data_f.write(test_name + '\n')
573
Azim Khan599cd242017-07-06 17:34:27 +0100574 # Write deps
575 dep_check_code += write_deps(out_data_f, test_deps, unique_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100576
Azim Khan599cd242017-07-06 17:34:27 +0100577 # Write test function name
578 test_function_name = 'test_' + function_name
579 assert test_function_name in func_info, "Function %s not found!" % test_function_name
580 func_id, func_args = func_info[test_function_name]
581 out_data_f.write(str(func_id))
582
583 # Write parameters
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100584 assert len(test_args) == len(func_args), \
585 "Invalid number of arguments in test %s. See function %s signature." % (test_name, function_name)
Azim Khan599cd242017-07-06 17:34:27 +0100586 expression_code += write_parameters(out_data_f, test_args, func_args, unique_expressions)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100587
Azim Khan599cd242017-07-06 17:34:27 +0100588 # Write a newline as test case separator
589 out_data_f.write('\n')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100590
Azim Khan599cd242017-07-06 17:34:27 +0100591 dep_check_code, expression_code = gen_suite_deps_checks(suite_deps, dep_check_code, expression_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100592 return dep_check_code, expression_code
593
594
Azim Khan1de892b2017-06-09 15:02:36 +0100595def generate_code(funcs_file, data_file, template_file, platform_file, help_file, suites_dir, c_file, out_data_file):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100596 """
597 Generate mbed-os test code.
598
599 :param funcs_file:
600 :param dat a_file:
601 :param template_file:
602 :param platform_file:
603 :param help_file:
604 :param suites_dir:
605 :param c_file:
606 :param out_data_file:
607 :return:
608 """
609 for name, path in [('Functions file', funcs_file),
610 ('Data file', data_file),
611 ('Template file', template_file),
612 ('Platform file', platform_file),
613 ('Help code file', help_file),
614 ('Suites dir', suites_dir)]:
615 if not os.path.exists(path):
616 raise IOError("ERROR: %s [%s] not found!" % (name, path))
617
618 snippets = {'generator_script' : os.path.basename(__file__)}
619
620 # Read helpers
621 with open(help_file, 'r') as help_f, open(platform_file, 'r') as platform_f:
622 snippets['test_common_helper_file'] = help_file
623 snippets['test_common_helpers'] = help_f.read()
624 snippets['test_platform_file'] = platform_file
625 snippets['platform_code'] = platform_f.read().replace('DATA_FILE',
626 out_data_file.replace('\\', '\\\\')) # escape '\'
627
628 # Function code
Azim Khanacc54732017-07-03 14:06:45 +0100629 with FileWrapper(funcs_file) as funcs_f, open(data_file, 'r') as data_f, open(out_data_file, 'w') as out_data_f:
Azim Khan13c6bfb2017-06-15 14:45:56 +0100630 suite_deps, dispatch_code, func_code, func_info = parse_functions(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100631 snippets['functions_code'] = func_code
632 snippets['dispatch_code'] = dispatch_code
Azim Khan13c6bfb2017-06-15 14:45:56 +0100633 dep_check_code, expression_code = gen_from_test_data(data_f, out_data_f, func_info, suite_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100634 snippets['dep_check_code'] = dep_check_code
635 snippets['expression_code'] = expression_code
636
637 snippets['test_file'] = c_file
638 snippets['test_main_file'] = template_file
639 snippets['test_case_file'] = funcs_file
640 snippets['test_case_data_file'] = data_file
641 # Read Template
642 # Add functions
643 #
644 with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
645 line_no = 1
646 for line in template_f.readlines():
647 snippets['line_no'] = line_no + 1 # Increment as it sets next line number
648 code = line.format(**snippets)
649 c_f.write(code)
650 line_no += 1
651
652
653def check_cmd():
654 """
655 Command line parser.
656
657 :return:
658 """
659 parser = argparse.ArgumentParser(description='Generate code for mbed-os tests.')
660
661 parser.add_argument("-f", "--functions-file",
662 dest="funcs_file",
663 help="Functions file",
664 metavar="FUNCTIONS",
665 required=True)
666
667 parser.add_argument("-d", "--data-file",
668 dest="data_file",
669 help="Data file",
670 metavar="DATA",
671 required=True)
672
673 parser.add_argument("-t", "--template-file",
674 dest="template_file",
675 help="Template file",
676 metavar="TEMPLATE",
677 required=True)
678
679 parser.add_argument("-s", "--suites-dir",
680 dest="suites_dir",
681 help="Suites dir",
682 metavar="SUITES",
683 required=True)
684
685 parser.add_argument("--help-file",
686 dest="help_file",
687 help="Help file",
688 metavar="HELPER",
689 required=True)
690
691 parser.add_argument("-p", "--platform-file",
692 dest="platform_file",
693 help="Platform code file",
694 metavar="PLATFORM_FILE",
695 required=True)
696
697 parser.add_argument("-o", "--out-dir",
698 dest="out_dir",
699 help="Dir where generated code and scripts are copied",
700 metavar="OUT_DIR",
701 required=True)
702
703 args = parser.parse_args()
704
705 data_file_name = os.path.basename(args.data_file)
706 data_name = os.path.splitext(data_file_name)[0]
707
708 out_c_file = os.path.join(args.out_dir, data_name + '.c')
709 out_data_file = os.path.join(args.out_dir, data_file_name)
710
711 out_c_file_dir = os.path.dirname(out_c_file)
712 out_data_file_dir = os.path.dirname(out_data_file)
713 for d in [out_c_file_dir, out_data_file_dir]:
714 if not os.path.exists(d):
715 os.makedirs(d)
716
Azim Khan1de892b2017-06-09 15:02:36 +0100717 generate_code(args.funcs_file, args.data_file, args.template_file, args.platform_file,
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100718 args.help_file, args.suites_dir, out_c_file, out_data_file)
719
720
721if __name__ == "__main__":
722 check_cmd()