mbed TLS Build
Dependents: Encrypt_Decrypt1 mbed_blink_tls encrypt encrypt
Diff: tests/scripts/generate_code.pl
- Revision:
- 0:cdf462088d13
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/scripts/generate_code.pl Thu Jan 05 00:18:44 2017 +0000 @@ -0,0 +1,373 @@ +#!/usr/bin/env perl + +# generate_code.pl +# +# This file is part of mbed TLS (https://tls.mbed.org) +# +# Copyright (c) 2009-2016, ARM Limited, All Rights Reserved +# +# Purpose +# +# Generates the test suite code given inputs of the test suite directory that +# contain the test suites, and the test suite file names for the test code and +# test data. +# +# Usage: generate_code.pl <suite dir> <code file> <data file> [main code file] +# +# Structure of files +# +# - main code file - 'main_test.function' +# Template file that contains the main() function for the test suite, +# test dispatch code as well as support functions. It contains the +# following symbols which are substituted by this script during +# processing: +# TESTCASE_FILENAME +# TESTCODE_FILENAME +# SUITE_PRE_DEP +# MAPPING_CODE +# FUNCTION CODE +# SUITE_POST_DEP +# DEP_CHECK_CODE +# DISPATCH_FUNCTION +# !LINE_NO! +# +# - common helper code file - 'helpers.function' +# Common helper functions +# +# - test suite code file - file name in the form 'test_suite_xxx.function' +# Code file that contains the actual test cases. The file contains a +# series of code sequences delimited by the following: +# BEGIN_HEADER / END_HEADER - list of headers files +# BEGIN_SUITE_HELPERS / END_SUITE_HELPERS - helper functions common to +# the test suite +# BEGIN_CASE / END_CASE - the test cases in the test suite. Each test +# case contains at least one function that is used to create the +# dispatch code. +# +# - test data file - file name in the form 'test_suite_xxxx.data' +# The test case parameters to to be used in execution of the test. The +# file name is used to replace the symbol 'TESTCASE_FILENAME' in the main +# code file above. +# + +use strict; + +my $suite_dir = shift or die "Missing suite directory"; +my $suite_name = shift or die "Missing suite name"; +my $data_name = shift or die "Missing data name"; +my $test_main_file = do { my $arg = shift; defined($arg) ? $arg : $suite_dir."/main_test.function" }; +my $test_file = $data_name.".c"; +my $test_common_helper_file = $suite_dir."/helpers.function"; +my $test_case_file = $suite_dir."/".$suite_name.".function"; +my $test_case_data = $suite_dir."/".$data_name.".data"; + +my $line_separator = $/; +undef $/; + + +# +# Open and read in the input files +# + +open(TEST_HELPERS, "$test_common_helper_file") or die "Opening test helpers +'$test_common_helper_file': $!"; +my $test_common_helpers = <TEST_HELPERS>; +close(TEST_HELPERS); + +open(TEST_MAIN, "$test_main_file") or die "Opening test main '$test_main_file': $!"; +my @test_main_lines = split/^/, <TEST_MAIN>; +my $test_main; +my $index = 2; +for my $line (@test_main_lines) { + $line =~ s/!LINE_NO!/$index/; + $test_main = $test_main.$line; + $index++; +} +close(TEST_MAIN); + +open(TEST_CASES, "$test_case_file") or die "Opening test cases '$test_case_file': $!"; +my @test_cases_lines = split/^/, <TEST_CASES>; +my $test_cases; +my $index = 2; +for my $line (@test_cases_lines) { + if ($line =~ /^\/\* BEGIN_SUITE_HELPERS .*\*\//) + { + $line = $line."#line $index \"$test_case_file\"\n"; + } + + if ($line =~ /^\/\* BEGIN_CASE .*\*\//) + { + $line = $line."#line $index \"$test_case_file\"\n"; + } + + $line =~ s/!LINE_NO!/$index/; + + $test_cases = $test_cases.$line; + $index++; +} + +close(TEST_CASES); + +open(TEST_DATA, "$test_case_data") or die "Opening test data '$test_case_data': $!"; +my $test_data = <TEST_DATA>; +close(TEST_DATA); + + +# +# Find the headers, dependencies, and suites in the test cases file +# + +my ( $suite_header ) = $test_cases =~ /\/\* BEGIN_HEADER \*\/\n(.*?)\n\/\* END_HEADER \*\//s; +my ( $suite_defines ) = $test_cases =~ /\/\* BEGIN_DEPENDENCIES\n \* (.*?)\n \* END_DEPENDENCIES/s; +my ( $suite_helpers ) = $test_cases =~ /\/\* BEGIN_SUITE_HELPERS \*\/\n(.*?)\n\/\* END_SUITE_HELPERS \*\//s; + +my $requirements; +if ($suite_defines =~ /^depends_on:/) +{ + ( $requirements ) = $suite_defines =~ /^depends_on:(.*)$/; +} + +my @var_req_arr = split(/:/, $requirements); +my $suite_pre_code; +my $suite_post_code; +my $dispatch_code; +my $mapping_code; +my %mapping_values; + +while (@var_req_arr) +{ + my $req = shift @var_req_arr; + $req =~ s/(!?)(.*)/$1defined($2)/; + + $suite_pre_code .= "#if $req\n"; + $suite_post_code .= "#endif /* $req */\n"; +} + +$/ = $line_separator; + +open(TEST_FILE, ">$test_file") or die "Opening destination file '$test_file': $!"; +print TEST_FILE << "END"; +/* + * *** THIS FILE HAS BEEN MACHINE GENERATED *** + * + * This file has been machine generated using the script: $0 + * + * Test file : $test_file + * + * The following files were used to create this file. + * + * Main code file : $test_main_file + * Helper file : $test_common_helper_file + * Test suite file : $test_case_file + * Test suite data : $test_case_data + * + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include <mbedtls/config.h> +#else +#include MBEDTLS_CONFIG_FILE +#endif + + +/*----------------------------------------------------------------------------*/ +/* Common helper code */ + +$test_common_helpers + + +/*----------------------------------------------------------------------------*/ +/* Test Suite Code */ + +$suite_pre_code +$suite_header +$suite_helpers +$suite_post_code + +END + +$test_main =~ s/SUITE_PRE_DEP/$suite_pre_code/; +$test_main =~ s/SUITE_POST_DEP/$suite_post_code/; + +while($test_cases =~ /\/\* BEGIN_CASE *([\w:]*) \*\/\n(.*?)\n\/\* END_CASE \*\//msg) +{ + my $function_deps = $1; + my $function_decl = $2; + + # Sanity checks of function + if ($function_decl !~ /^#line\s*.*\nvoid /) + { + die "Test function does not have 'void' as return type.\n" . + "Function declaration:\n" . + $function_decl; + } + if ($function_decl !~ /^(#line\s*.*)\nvoid (\w+)\(\s*(.*?)\s*\)\s*{(.*)}/ms) + { + die "Function declaration not in expected format\n"; + } + my $line_directive = $1; + my $function_name = $2; + my $function_params = $3; + my $function_pre_code; + my $function_post_code; + my $param_defs; + my $param_checks; + my @dispatch_params; + my @var_def_arr = split(/,\s*/, $function_params); + my $i = 1; + my $mapping_regex = "".$function_name; + my $mapping_count = 0; + + $function_decl =~ s/(^#line\s*.*)\nvoid /$1\nvoid test_suite_/; + + # Add exit label if not present + if ($function_decl !~ /^exit:$/m) + { + $function_decl =~ s/}\s*$/\nexit:\n return;\n}/; + } + + if ($function_deps =~ /^depends_on:/) + { + ( $function_deps ) = $function_deps =~ /^depends_on:(.*)$/; + } + + foreach my $req (split(/:/, $function_deps)) + { + $function_pre_code .= "#ifdef $req\n"; + $function_post_code .= "#endif /* $req */\n"; + } + + foreach my $def (@var_def_arr) + { + # Handle the different parameter types + if( substr($def, 0, 4) eq "int " ) + { + $param_defs .= " int param$i;\n"; + $param_checks .= " if( verify_int( params[$i], ¶m$i ) != 0 ) return( DISPATCH_INVALID_TEST_DATA );\n"; + push @dispatch_params, "param$i"; + + $mapping_regex .= ":([\\d\\w |\\+\\-\\(\\)]+)"; + $mapping_count++; + } + elsif( substr($def, 0, 6) eq "char *" ) + { + $param_defs .= " char *param$i = params[$i];\n"; + $param_checks .= " if( verify_string( ¶m$i ) != 0 ) return( DISPATCH_INVALID_TEST_DATA );\n"; + push @dispatch_params, "param$i"; + $mapping_regex .= ":[^:\n]+"; + } + else + { + die "Parameter declaration not of supported type (int, char *)\n"; + } + $i++; + + } + + # Find non-integer values we should map for this function + if( $mapping_count) + { + my @res = $test_data =~ /^$mapping_regex/msg; + foreach my $value (@res) + { + next unless ($value !~ /^\d+$/); + if ( $mapping_values{$value} ) { + ${ $mapping_values{$value} }{$function_pre_code} = 1; + } else { + $mapping_values{$value} = { $function_pre_code => 1 }; + } + } + } + + my $call_params = join ", ", @dispatch_params; + my $param_count = @var_def_arr + 1; + $dispatch_code .= << "END"; +if( strcmp( params[0], "$function_name" ) == 0 ) +{ +$function_pre_code +$param_defs + if( cnt != $param_count ) + { + mbedtls_fprintf( stderr, "\\nIncorrect argument count (%d != %d)\\n", cnt, $param_count ); + return( DISPATCH_INVALID_TEST_DATA ); + } + +$param_checks + test_suite_$function_name( $call_params ); + return ( DISPATCH_TEST_SUCCESS ); +$function_post_code + return ( DISPATCH_UNSUPPORTED_SUITE ); +} +else +END + + my $function_code = $function_pre_code . $function_decl . "\n" . + $function_post_code; + $test_main =~ s/FUNCTION_CODE/$function_code\nFUNCTION_CODE/; +} + +# Find specific case dependencies that we should be able to check +# and make check code +my $dep_check_code; + +my @res = $test_data =~ /^depends_on:([\w:]+)/msg; +my %case_deps; +foreach my $deps (@res) +{ + foreach my $dep (split(/:/, $deps)) + { + $case_deps{$dep} = 1; + } +} +while( my ($key, $value) = each(%case_deps) ) +{ + $dep_check_code .= << "END"; + if( strcmp( str, "$key" ) == 0 ) + { +#if defined($key) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif + } +END +} + +# Make mapping code +while( my ($key, $value) = each(%mapping_values) ) +{ + my $key_mapping_code = << "END"; + if( strcmp( str, "$key" ) == 0 ) + { + *value = ( $key ); + return( KEY_VALUE_MAPPING_FOUND ); + } +END + + # handle depenencies, unless used at least one without depends + if ($value->{""}) { + $mapping_code .= $key_mapping_code; + next; + } + for my $ifdef ( keys %$value ) { + (my $endif = $ifdef) =~ s!ifdef!endif //!g; + $mapping_code .= $ifdef . $key_mapping_code . $endif; + } +} + +$dispatch_code =~ s/^(.+)/ $1/mg; + +$test_main =~ s/TESTCASE_FILENAME/$test_case_data/g; +$test_main =~ s/TESTCODE_FILENAME/$test_case_file/g; +$test_main =~ s/FUNCTION_CODE//; +$test_main =~ s/DEP_CHECK_CODE/$dep_check_code/; +$test_main =~ s/DISPATCH_FUNCTION/$dispatch_code/; +$test_main =~ s/MAPPING_CODE/$mapping_code/; + +print TEST_FILE << "END"; +$test_main +END + +close(TEST_FILE);