generate_code.pl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #!/usr/bin/env perl
  2. # generate_code.pl
  3. #
  4. # This file is part of mbed TLS (https://tls.mbed.org)
  5. #
  6. # Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
  7. #
  8. # Purpose
  9. #
  10. # Generates the test suite code given inputs of the test suite directory that
  11. # contain the test suites, and the test suite file names for the test code and
  12. # test data.
  13. #
  14. # Usage: generate_code.pl <suite dir> <code file> <data file> [main code file]
  15. #
  16. # Structure of files
  17. #
  18. # - main code file - 'main_test.function'
  19. # Template file that contains the main() function for the test suite,
  20. # test dispatch code as well as support functions. It contains the
  21. # following symbols which are substituted by this script during
  22. # processing:
  23. # TESTCASE_FILENAME
  24. # TESTCODE_FILENAME
  25. # SUITE_PRE_DEP
  26. # MAPPING_CODE
  27. # FUNCTION CODE
  28. # SUITE_POST_DEP
  29. # DEP_CHECK_CODE
  30. # DISPATCH_FUNCTION
  31. # !LINE_NO!
  32. #
  33. # - common helper code file - 'helpers.function'
  34. # Common helper functions
  35. #
  36. # - test suite code file - file name in the form 'test_suite_xxx.function'
  37. # Code file that contains the actual test cases. The file contains a
  38. # series of code sequences delimited by the following:
  39. # BEGIN_HEADER / END_HEADER - list of headers files
  40. # BEGIN_SUITE_HELPERS / END_SUITE_HELPERS - helper functions common to
  41. # the test suite
  42. # BEGIN_CASE / END_CASE - the test cases in the test suite. Each test
  43. # case contains at least one function that is used to create the
  44. # dispatch code.
  45. #
  46. # - test data file - file name in the form 'test_suite_xxxx.data'
  47. # The test case parameters to to be used in execution of the test. The
  48. # file name is used to replace the symbol 'TESTCASE_FILENAME' in the main
  49. # code file above.
  50. #
  51. use strict;
  52. my $suite_dir = shift or die "Missing suite directory";
  53. my $suite_name = shift or die "Missing suite name";
  54. my $data_name = shift or die "Missing data name";
  55. my $test_main_file = do { my $arg = shift; defined($arg) ? $arg : $suite_dir."/main_test.function" };
  56. my $test_file = $data_name.".c";
  57. my $test_common_helper_file = $suite_dir."/helpers.function";
  58. my $test_case_file = $suite_dir."/".$suite_name.".function";
  59. my $test_case_data = $suite_dir."/".$data_name.".data";
  60. my $line_separator = $/;
  61. undef $/;
  62. #
  63. # Open and read in the input files
  64. #
  65. open(TEST_HELPERS, "$test_common_helper_file") or die "Opening test helpers
  66. '$test_common_helper_file': $!";
  67. my $test_common_helpers = <TEST_HELPERS>;
  68. close(TEST_HELPERS);
  69. open(TEST_MAIN, "$test_main_file") or die "Opening test main '$test_main_file': $!";
  70. my @test_main_lines = split/^/, <TEST_MAIN>;
  71. my $test_main;
  72. my $index = 2;
  73. for my $line (@test_main_lines) {
  74. $line =~ s/!LINE_NO!/$index/;
  75. $test_main = $test_main.$line;
  76. $index++;
  77. }
  78. close(TEST_MAIN);
  79. open(TEST_CASES, "$test_case_file") or die "Opening test cases '$test_case_file': $!";
  80. my @test_cases_lines = split/^/, <TEST_CASES>;
  81. my $test_cases;
  82. my $index = 2;
  83. for my $line (@test_cases_lines) {
  84. if ($line =~ /^\/\* BEGIN_SUITE_HELPERS .*\*\//)
  85. {
  86. $line = $line."#line $index \"$test_case_file\"\n";
  87. }
  88. if ($line =~ /^\/\* BEGIN_CASE .*\*\//)
  89. {
  90. $line = $line."#line $index \"$test_case_file\"\n";
  91. }
  92. $line =~ s/!LINE_NO!/$index/;
  93. $test_cases = $test_cases.$line;
  94. $index++;
  95. }
  96. close(TEST_CASES);
  97. open(TEST_DATA, "$test_case_data") or die "Opening test data '$test_case_data': $!";
  98. my $test_data = <TEST_DATA>;
  99. close(TEST_DATA);
  100. #
  101. # Find the headers, dependencies, and suites in the test cases file
  102. #
  103. my ( $suite_header ) = $test_cases =~ /\/\* BEGIN_HEADER \*\/\n(.*?)\n\/\* END_HEADER \*\//s;
  104. my ( $suite_defines ) = $test_cases =~ /\/\* BEGIN_DEPENDENCIES\n \* (.*?)\n \* END_DEPENDENCIES/s;
  105. my ( $suite_helpers ) = $test_cases =~ /\/\* BEGIN_SUITE_HELPERS \*\/\n(.*?)\n\/\* END_SUITE_HELPERS \*\//s;
  106. my $requirements;
  107. if ($suite_defines =~ /^depends_on:/)
  108. {
  109. ( $requirements ) = $suite_defines =~ /^depends_on:(.*)$/;
  110. }
  111. my @var_req_arr = split(/:/, $requirements);
  112. my $suite_pre_code;
  113. my $suite_post_code;
  114. my $dispatch_code;
  115. my $mapping_code;
  116. my %mapping_values;
  117. while (@var_req_arr)
  118. {
  119. my $req = shift @var_req_arr;
  120. $req =~ s/(!?)(.*)/$1defined($2)/;
  121. $suite_pre_code .= "#if $req\n";
  122. $suite_post_code .= "#endif /* $req */\n";
  123. }
  124. $/ = $line_separator;
  125. open(TEST_FILE, ">$test_file") or die "Opening destination file '$test_file': $!";
  126. print TEST_FILE << "END";
  127. /*
  128. * *** THIS FILE HAS BEEN MACHINE GENERATED ***
  129. *
  130. * This file has been machine generated using the script: $0
  131. *
  132. * Test file : $test_file
  133. *
  134. * The following files were used to create this file.
  135. *
  136. * Main code file : $test_main_file
  137. * Helper file : $test_common_helper_file
  138. * Test suite file : $test_case_file
  139. * Test suite data : $test_case_data
  140. *
  141. *
  142. * This file is part of mbed TLS (https://tls.mbed.org)
  143. */
  144. #if !defined(MBEDTLS_CONFIG_FILE)
  145. #include <mbedtls/config.h>
  146. #else
  147. #include MBEDTLS_CONFIG_FILE
  148. #endif
  149. /*----------------------------------------------------------------------------*/
  150. /* Common helper code */
  151. $test_common_helpers
  152. /*----------------------------------------------------------------------------*/
  153. /* Test Suite Code */
  154. $suite_pre_code
  155. $suite_header
  156. $suite_helpers
  157. $suite_post_code
  158. END
  159. $test_main =~ s/SUITE_PRE_DEP/$suite_pre_code/;
  160. $test_main =~ s/SUITE_POST_DEP/$suite_post_code/;
  161. while($test_cases =~ /\/\* BEGIN_CASE *([\w:]*) \*\/\n(.*?)\n\/\* END_CASE \*\//msg)
  162. {
  163. my $function_deps = $1;
  164. my $function_decl = $2;
  165. # Sanity checks of function
  166. if ($function_decl !~ /^#line\s*.*\nvoid /)
  167. {
  168. die "Test function does not have 'void' as return type.\n" .
  169. "Function declaration:\n" .
  170. $function_decl;
  171. }
  172. if ($function_decl !~ /^(#line\s*.*)\nvoid (\w+)\(\s*(.*?)\s*\)\s*{(.*)}/ms)
  173. {
  174. die "Function declaration not in expected format\n";
  175. }
  176. my $line_directive = $1;
  177. my $function_name = $2;
  178. my $function_params = $3;
  179. my $function_pre_code;
  180. my $function_post_code;
  181. my $param_defs;
  182. my $param_checks;
  183. my @dispatch_params;
  184. my @var_def_arr = split(/,\s*/, $function_params);
  185. my $i = 1;
  186. my $mapping_regex = "".$function_name;
  187. my $mapping_count = 0;
  188. $function_decl =~ s/(^#line\s*.*)\nvoid /$1\nvoid test_suite_/;
  189. # Add exit label if not present
  190. if ($function_decl !~ /^exit:$/m)
  191. {
  192. $function_decl =~ s/}\s*$/\nexit:\n return;\n}/;
  193. }
  194. if ($function_deps =~ /^depends_on:/)
  195. {
  196. ( $function_deps ) = $function_deps =~ /^depends_on:(.*)$/;
  197. }
  198. foreach my $req (split(/:/, $function_deps))
  199. {
  200. $function_pre_code .= "#ifdef $req\n";
  201. $function_post_code .= "#endif /* $req */\n";
  202. }
  203. foreach my $def (@var_def_arr)
  204. {
  205. # Handle the different parameter types
  206. if( substr($def, 0, 4) eq "int " )
  207. {
  208. $param_defs .= " int param$i;\n";
  209. $param_checks .= " if( verify_int( params[$i], &param$i ) != 0 ) return( DISPATCH_INVALID_TEST_DATA );\n";
  210. push @dispatch_params, "param$i";
  211. $mapping_regex .= ":([\\d\\w |\\+\\-\\(\\)]+)";
  212. $mapping_count++;
  213. }
  214. elsif( substr($def, 0, 6) eq "char *" )
  215. {
  216. $param_defs .= " char *param$i = params[$i];\n";
  217. $param_checks .= " if( verify_string( &param$i ) != 0 ) return( DISPATCH_INVALID_TEST_DATA );\n";
  218. push @dispatch_params, "param$i";
  219. $mapping_regex .= ":[^:\n]+";
  220. }
  221. else
  222. {
  223. die "Parameter declaration not of supported type (int, char *)\n";
  224. }
  225. $i++;
  226. }
  227. # Find non-integer values we should map for this function
  228. if( $mapping_count)
  229. {
  230. my @res = $test_data =~ /^$mapping_regex/msg;
  231. foreach my $value (@res)
  232. {
  233. next unless ($value !~ /^\d+$/);
  234. if ( $mapping_values{$value} ) {
  235. ${ $mapping_values{$value} }{$function_pre_code} = 1;
  236. } else {
  237. $mapping_values{$value} = { $function_pre_code => 1 };
  238. }
  239. }
  240. }
  241. my $call_params = join ", ", @dispatch_params;
  242. my $param_count = @var_def_arr + 1;
  243. $dispatch_code .= << "END";
  244. if( strcmp( params[0], "$function_name" ) == 0 )
  245. {
  246. $function_pre_code
  247. $param_defs
  248. if( cnt != $param_count )
  249. {
  250. mbedtls_fprintf( stderr, "\\nIncorrect argument count (%d != %d)\\n", cnt, $param_count );
  251. return( DISPATCH_INVALID_TEST_DATA );
  252. }
  253. $param_checks
  254. test_suite_$function_name( $call_params );
  255. return ( DISPATCH_TEST_SUCCESS );
  256. $function_post_code
  257. return ( DISPATCH_UNSUPPORTED_SUITE );
  258. }
  259. else
  260. END
  261. my $function_code = $function_pre_code . $function_decl . "\n" .
  262. $function_post_code;
  263. $test_main =~ s/FUNCTION_CODE/$function_code\nFUNCTION_CODE/;
  264. }
  265. # Find specific case dependencies that we should be able to check
  266. # and make check code
  267. my $dep_check_code;
  268. my @res = $test_data =~ /^depends_on:([\w:]+)/msg;
  269. my %case_deps;
  270. foreach my $deps (@res)
  271. {
  272. foreach my $dep (split(/:/, $deps))
  273. {
  274. $case_deps{$dep} = 1;
  275. }
  276. }
  277. while( my ($key, $value) = each(%case_deps) )
  278. {
  279. $dep_check_code .= << "END";
  280. if( strcmp( str, "$key" ) == 0 )
  281. {
  282. #if defined($key)
  283. return( DEPENDENCY_SUPPORTED );
  284. #else
  285. return( DEPENDENCY_NOT_SUPPORTED );
  286. #endif
  287. }
  288. END
  289. }
  290. # Make mapping code
  291. while( my ($key, $value) = each(%mapping_values) )
  292. {
  293. my $key_mapping_code = << "END";
  294. if( strcmp( str, "$key" ) == 0 )
  295. {
  296. *value = ( $key );
  297. return( KEY_VALUE_MAPPING_FOUND );
  298. }
  299. END
  300. # handle depenencies, unless used at least one without depends
  301. if ($value->{""}) {
  302. $mapping_code .= $key_mapping_code;
  303. next;
  304. }
  305. for my $ifdef ( keys %$value ) {
  306. (my $endif = $ifdef) =~ s!ifdef!endif //!g;
  307. $mapping_code .= $ifdef . $key_mapping_code . $endif;
  308. }
  309. }
  310. $dispatch_code =~ s/^(.+)/ $1/mg;
  311. $test_main =~ s/TESTCASE_FILENAME/$test_case_data/g;
  312. $test_main =~ s/TESTCODE_FILENAME/$test_case_file/g;
  313. $test_main =~ s/FUNCTION_CODE//;
  314. $test_main =~ s/DEP_CHECK_CODE/$dep_check_code/;
  315. $test_main =~ s/DISPATCH_FUNCTION/$dispatch_code/;
  316. $test_main =~ s/MAPPING_CODE/$mapping_code/;
  317. print TEST_FILE << "END";
  318. $test_main
  319. END
  320. close(TEST_FILE);