mirror of
https://github.com/yuzu-emu/mbedtls.git
synced 2024-11-27 06:24:20 +01:00
2ba437ad3c
Generate proper #line directives in all BEGIN_xxx blocks. Before, there were #line directives for BEGIN_SUITE_HELPERS and BEGIN_CASE but not BEGIN_HEADER, so debug information for code in the header block pointed inside helpers.function instead of the test's source file.
407 lines
12 KiB
Perl
Executable File
407 lines
12 KiB
Perl
Executable File
#!/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.
|
|
#
|
|
# A test data file consists of a sequence of paragraphs separated by
|
|
# a single empty line. Line breaks may be in Unix (LF) or Windows (CRLF)
|
|
# format. Lines starting with the character '#' are ignored
|
|
# (the parser behaves as if they were not present).
|
|
#
|
|
# Each paragraph describes one test case and must consist of: (1) one
|
|
# line which is the test case name; (2) an optional line starting with
|
|
# the 11-character prefix "depends_on:"; (3) a line containing the test
|
|
# function to execute and its parameters.
|
|
#
|
|
# A depends_on: line consists of a list of compile-time options
|
|
# separated by the character ':', with no whitespace. The test case
|
|
# is executed only if this compilation option is enabled in config.h.
|
|
#
|
|
# The last line of each paragraph contains a test function name and
|
|
# a list of parameters separated by the character ':'. Running the
|
|
# test case calls this function with the specified parameters. Each
|
|
# parameter may either be an integer written in decimal or hexadecimal,
|
|
# or a string surrounded by double quotes which may not contain the
|
|
# ':' character.
|
|
#
|
|
|
|
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_.*\*\//)
|
|
{
|
|
$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) )
|
|
{
|
|
if( substr($key, 0, 1) eq "!" )
|
|
{
|
|
my $key = substr($key, 1);
|
|
$dep_check_code .= << "END";
|
|
if( strcmp( str, "!$key" ) == 0 )
|
|
{
|
|
#if !defined($key)
|
|
return( DEPENDENCY_SUPPORTED );
|
|
#else
|
|
return( DEPENDENCY_NOT_SUPPORTED );
|
|
#endif
|
|
}
|
|
END
|
|
}
|
|
else
|
|
{
|
|
$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);
|