blob: f81ec91cb8d6b01476ed384c2869b093bae5b7cf [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 = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100430 case {id}:
431 {{
432 #if {noT}defined({macro})
433 ret = DEPENDENCY_SUPPORTED;
434 #else
435 ret = DEPENDENCY_NOT_SUPPORTED;
436 #endif
437 }}
438 break;'''.format(noT=noT, macro=dep, id=dep_id)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100439 return dep_check
440
441
442def gen_expression_check(exp_id, exp):
443 """
444 Generates code for expression check
445
446 :param exp_id:
447 :param exp:
448 :return:
449 """
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100450 assert exp_id > -1, "Expression Id should be a positive integer."
451 assert len(exp) > 0, "Expression should not be an empty string."
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100452 exp_code = '''
Azim Khanb1c2d0f2017-07-07 17:14:02 +0100453 case {exp_id}:
454 {{
455 *out_value = {expression};
456 }}
457 break;'''.format(exp_id=exp_id, expression=exp)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100458 return exp_code
459
460
Azim Khan599cd242017-07-06 17:34:27 +0100461def write_deps(out_data_f, test_deps, unique_deps):
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100462 """
Azim Khan599cd242017-07-06 17:34:27 +0100463 Write dependencies to intermediate test data file.
464 It also returns dependency check code.
465
466 :param out_data_f:
467 :param dep:
468 :param unique_deps:
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100469 :return:
470 """
Azim Khan599cd242017-07-06 17:34:27 +0100471 dep_check_code = ''
472 if len(test_deps):
473 out_data_f.write('depends_on')
474 for dep in test_deps:
475 if dep not in unique_deps:
476 unique_deps.append(dep)
477 dep_id = unique_deps.index(dep)
478 dep_check_code += gen_dep_check(dep_id, dep)
479 else:
480 dep_id = unique_deps.index(dep)
481 out_data_f.write(':' + str(dep_id))
482 out_data_f.write('\n')
483 return dep_check_code
484
485
486def write_parameters(out_data_f, test_args, func_args, unique_expressions):
487 """
488 Writes test parameters to the intermediate data file.
489 Also generates expression code.
490
491 :param out_data_f:
492 :param test_args:
493 :param func_args:
494 :param unique_expressions:
495 :return:
496 """
497 expression_code = ''
498 for i in xrange(len(test_args)):
499 typ = func_args[i]
500 val = test_args[i]
501
502 # check if val is a non literal int val
503 if typ == 'int' and not re.match('(\d+$)|((0x)?[0-9a-fA-F]+$)', val): # its an expression
504 typ = 'exp'
505 if val not in unique_expressions:
506 unique_expressions.append(val)
507 # exp_id can be derived from len(). But for readability and consistency with case of existing let's
508 # use index().
509 exp_id = unique_expressions.index(val)
510 expression_code += gen_expression_check(exp_id, val)
511 val = exp_id
512 else:
513 val = unique_expressions.index(val)
514 out_data_f.write(':' + typ + ':' + str(val))
515 out_data_f.write('\n')
516 return expression_code
517
518
519def gen_suite_deps_checks(suite_deps, dep_check_code, expression_code):
520 """
521 Adds preprocessor checks for test suite dependencies.
522
523 :param suite_deps:
524 :param dep_check_code:
525 :param expression_code:
526 :return:
527 """
Azim Khan599cd242017-07-06 17:34:27 +0100528 if len(suite_deps):
529 ifdef = gen_deps_one_line(suite_deps)
530 dep_check_code = '''
531{ifdef}
532{code}
Azim Khan599cd242017-07-06 17:34:27 +0100533#endif
534'''.format(ifdef=ifdef, code=dep_check_code)
535 expression_code = '''
536{ifdef}
537{code}
Azim Khan599cd242017-07-06 17:34:27 +0100538#endif
539'''.format(ifdef=ifdef, code=expression_code)
540 return dep_check_code, expression_code
Azim Khan5e2ac1f2017-07-03 13:58:20 +0100541
542
Azim Khan13c6bfb2017-06-15 14:45:56 +0100543def gen_from_test_data(data_f, out_data_f, func_info, suite_deps):
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100544 """
545 Generates dependency checks, expression code and intermediate data file from test data file.
546
547 :param data_f:
548 :param out_data_f:
549 :param func_info:
Azim Khan13c6bfb2017-06-15 14:45:56 +0100550 :param suite_deps:
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100551 :return:
552 """
553 unique_deps = []
554 unique_expressions = []
555 dep_check_code = ''
556 expression_code = ''
557 for test_name, function_name, test_deps, test_args in parse_test_data(data_f):
558 out_data_f.write(test_name + '\n')
559
Azim Khan599cd242017-07-06 17:34:27 +0100560 # Write deps
561 dep_check_code += write_deps(out_data_f, test_deps, unique_deps)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100562
Azim Khan599cd242017-07-06 17:34:27 +0100563 # Write test function name
564 test_function_name = 'test_' + function_name
565 assert test_function_name in func_info, "Function %s not found!" % test_function_name
566 func_id, func_args = func_info[test_function_name]
567 out_data_f.write(str(func_id))
568
569 # Write parameters
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100570 assert len(test_args) == len(func_args), \
571 "Invalid number of arguments in test %s. See function %s signature." % (test_name, function_name)
Azim Khan599cd242017-07-06 17:34:27 +0100572 expression_code += write_parameters(out_data_f, test_args, func_args, unique_expressions)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100573
Azim Khan599cd242017-07-06 17:34:27 +0100574 # Write a newline as test case separator
575 out_data_f.write('\n')
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100576
Azim Khan599cd242017-07-06 17:34:27 +0100577 dep_check_code, expression_code = gen_suite_deps_checks(suite_deps, dep_check_code, expression_code)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100578 return dep_check_code, expression_code
579
580
Azim Khan1de892b2017-06-09 15:02:36 +0100581def 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 +0100582 """
583 Generate mbed-os test code.
584
585 :param funcs_file:
586 :param dat a_file:
587 :param template_file:
588 :param platform_file:
589 :param help_file:
590 :param suites_dir:
591 :param c_file:
592 :param out_data_file:
593 :return:
594 """
595 for name, path in [('Functions file', funcs_file),
596 ('Data file', data_file),
597 ('Template file', template_file),
598 ('Platform file', platform_file),
599 ('Help code file', help_file),
600 ('Suites dir', suites_dir)]:
601 if not os.path.exists(path):
602 raise IOError("ERROR: %s [%s] not found!" % (name, path))
603
604 snippets = {'generator_script' : os.path.basename(__file__)}
605
606 # Read helpers
607 with open(help_file, 'r') as help_f, open(platform_file, 'r') as platform_f:
608 snippets['test_common_helper_file'] = help_file
609 snippets['test_common_helpers'] = help_f.read()
610 snippets['test_platform_file'] = platform_file
611 snippets['platform_code'] = platform_f.read().replace('DATA_FILE',
612 out_data_file.replace('\\', '\\\\')) # escape '\'
613
614 # Function code
Azim Khanacc54732017-07-03 14:06:45 +0100615 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 +0100616 suite_deps, dispatch_code, func_code, func_info = parse_functions(funcs_f)
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100617 snippets['functions_code'] = func_code
618 snippets['dispatch_code'] = dispatch_code
Azim Khan13c6bfb2017-06-15 14:45:56 +0100619 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 +0100620 snippets['dep_check_code'] = dep_check_code
621 snippets['expression_code'] = expression_code
622
623 snippets['test_file'] = c_file
624 snippets['test_main_file'] = template_file
625 snippets['test_case_file'] = funcs_file
626 snippets['test_case_data_file'] = data_file
627 # Read Template
628 # Add functions
629 #
630 with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
631 line_no = 1
632 for line in template_f.readlines():
633 snippets['line_no'] = line_no + 1 # Increment as it sets next line number
634 code = line.format(**snippets)
635 c_f.write(code)
636 line_no += 1
637
638
639def check_cmd():
640 """
641 Command line parser.
642
643 :return:
644 """
645 parser = argparse.ArgumentParser(description='Generate code for mbed-os tests.')
646
647 parser.add_argument("-f", "--functions-file",
648 dest="funcs_file",
649 help="Functions file",
650 metavar="FUNCTIONS",
651 required=True)
652
653 parser.add_argument("-d", "--data-file",
654 dest="data_file",
655 help="Data file",
656 metavar="DATA",
657 required=True)
658
659 parser.add_argument("-t", "--template-file",
660 dest="template_file",
661 help="Template file",
662 metavar="TEMPLATE",
663 required=True)
664
665 parser.add_argument("-s", "--suites-dir",
666 dest="suites_dir",
667 help="Suites dir",
668 metavar="SUITES",
669 required=True)
670
671 parser.add_argument("--help-file",
672 dest="help_file",
673 help="Help file",
674 metavar="HELPER",
675 required=True)
676
677 parser.add_argument("-p", "--platform-file",
678 dest="platform_file",
679 help="Platform code file",
680 metavar="PLATFORM_FILE",
681 required=True)
682
683 parser.add_argument("-o", "--out-dir",
684 dest="out_dir",
685 help="Dir where generated code and scripts are copied",
686 metavar="OUT_DIR",
687 required=True)
688
689 args = parser.parse_args()
690
691 data_file_name = os.path.basename(args.data_file)
692 data_name = os.path.splitext(data_file_name)[0]
693
694 out_c_file = os.path.join(args.out_dir, data_name + '.c')
695 out_data_file = os.path.join(args.out_dir, data_file_name)
696
697 out_c_file_dir = os.path.dirname(out_c_file)
698 out_data_file_dir = os.path.dirname(out_data_file)
699 for d in [out_c_file_dir, out_data_file_dir]:
700 if not os.path.exists(d):
701 os.makedirs(d)
702
Azim Khan1de892b2017-06-09 15:02:36 +0100703 generate_code(args.funcs_file, args.data_file, args.template_file, args.platform_file,
Mohammad Azim Khanfff49042017-03-28 01:48:31 +0100704 args.help_file, args.suites_dir, out_c_file, out_data_file)
705
706
707if __name__ == "__main__":
708 check_cmd()