Merge branch 'master' into master

This commit is contained in:
Mark VanderVoord
2020-03-16 13:44:41 -04:00
committed by GitHub
13 changed files with 401 additions and 26 deletions
+19 -1
View File
@@ -32,6 +32,12 @@ class CMock
end
end
def setup_skeletons(files)
[files].flatten.each do |src|
generate_skeleton src
end
end
private ###############################
def generate_mock(src)
@@ -39,6 +45,12 @@ class CMock
puts "Creating mock for #{name}..." unless @silent
@cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)))
end
def generate_skeleton(src)
name = File.basename(src, '.h')
puts "Creating skeleton for #{name}..." unless @silent
@cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src)))
end
end
def option_maker(options, key, val)
@@ -75,6 +87,8 @@ if ($0 == __FILE__)
ARGV.each do |arg|
if (arg =~ /^-o\"?([a-zA-Z0-9._\\\/:\s]+)\"?/)
options.merge! CMockConfig.load_config_file_from_yaml( arg.gsub(/^-o/,'') )
elsif (arg == "--skeleton")
options[:skeleton] = true
elsif (arg =~ /^--([a-zA-Z0-9._\\\/:\s]+)=\"?([a-zA-Z0-9._\-\\\/:\s\;]+)\"?/)
options = option_maker(options, $1, $2)
else
@@ -82,5 +96,9 @@ if ($0 == __FILE__)
end
end
CMock.new(options).setup_mocks(filelist)
if (options[:skeleton])
CMock.new(options).setup_skeletons(filelist)
else
CMock.new(options).setup_mocks(filelist)
end
end
+2
View File
@@ -12,6 +12,7 @@ class CMockConfig
:mock_path => 'mocks',
:mock_prefix => 'Mock',
:mock_suffix => '',
:skeleton_path => '',
:weak => '',
:subdir => nil,
:plugins => [],
@@ -40,6 +41,7 @@ class CMockConfig
:orig_header_include_fmt => "#include \"%s\"",
:array_size_type => [],
:array_size_name => 'size|len',
:skeleton => false,
# Format to look for inline functions.
# This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static")
+8
View File
@@ -33,6 +33,14 @@ class CMockFileWriter
update_file(full_file_name_done, full_file_name_temp)
end
def append_file(filename, subdir)
raise "Where's the block of data to create?" unless block_given?
full_file_name = "#{@config.skeleton_path}/#{subdir+'/' if subdir}#{filename}"
File.open(full_file_name, 'a') do |file|
yield(file, filename)
end
end
private ###################################
def update_file(dest, src)
+37 -1
View File
@@ -55,6 +55,11 @@ class CMockGenerator
create_mock_source_file(parsed_stuff)
end
def create_skeleton(module_name, parsed_stuff)
@module_name = module_name
create_skeleton_source_file(parsed_stuff)
end
private if $ThisIsOnlyATest.nil? ##############################
def create_mock_subdir()
@@ -94,6 +99,17 @@ class CMockGenerator
end
end
def create_skeleton_source_file(parsed_stuff)
filename = "#{@config.mock_path}/#{@subdir+'/' if @subdir}#{module_name}.c"
existing = File.exists?(filename) ? File.read(filename) : ""
@file_writer.append_file(@module_name + ".c", @subdir) do |file, filename|
create_source_header_section(file, filename, []) if existing.empty?
parsed_stuff[:functions].each do |function|
create_function_skeleton(file, function, existing)
end
end
end
def create_mock_header_header(file, filename)
define_name = @clean_mock_name.upcase
orig_filename = (@subdir ? @subdir + "/" : "") + @module_name + ".h"
@@ -146,7 +162,7 @@ class CMockGenerator
def create_source_header_section(file, filename, functions)
header_file = (@subdir ? @subdir + '/' : '') + filename.gsub(".c",".h")
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n"
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless functions.empty?
file << "#include <string.h>\n"
file << "#include <stdlib.h>\n"
file << "#include <setjmp.h>\n"
@@ -272,4 +288,24 @@ class CMockGenerator
file << @utils.code_add_argument_loader(function)
file << @plugins.run(:mock_interfaces, function)
end
def create_function_skeleton(file, function, existing)
# prepare return value and arguments
function_mod_and_rettype = (function[:modifier].empty? ? '' : "#{function[:modifier]} ") +
(function[:return][:type]) +
(function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '')
args_string = function[:args_string]
args_string += (", " + function[:var_arg]) unless (function[:var_arg].nil?)
decl = "#{function_mod_and_rettype} #{function[:name]}(#{args_string})"
unless (existing.include?(decl))
file << "#{decl}\n"
file << "{\n"
file << " //TODO: Implement Me!\n"
function[:args].each {|arg| file << " (void)#{arg[:name]};\n"}
file << " return (#{(function[:return][:type])})0;\n" unless (function[:return][:void?])
file << "}\n\n"
end
end
end
+7
View File
@@ -79,4 +79,11 @@ class CMockGeneratorPluginCallback
lines << " Mock.#{func_name}_CallbackBool = (int)0;\n"
lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n"
end
def mock_verify(function)
func_name = function[:name]
" if (Mock.#{func_name}_CallbackFunctionPointer != NULL)\n {\n" \
" call_instance = CMOCK_GUTS_NONE;\n" \
" (void)call_instance;\n }\n"
end
end
+46 -13
View File
@@ -16,7 +16,8 @@ class CMockHeaderParser
@c_calling_conventions = cfg.c_calling_conventions.uniq
@treat_as_array = cfg.treat_as_array
@treat_as_void = (['void'] + cfg.treat_as_void).uniq
@declaration_parse_matcher = /([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+-]*)\)$/m
@function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+-]*)\)'
@declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m
@standards = (['int','short','char','long','unsigned','signed'] + cfg.treat_as.keys).uniq
@array_size_name = cfg.array_size_name
@array_size_type = (['int', 'size_t'] + cfg.array_size_type).uniq
@@ -107,31 +108,63 @@ class CMockHeaderParser
square_bracket_pair_regex_format = /\{[^\{\}]*\}/ # Regex to match one whole block enclosed by two square brackets
# Convert user provided string patterns to regex
# Use word bounderies before and after the user regex to limit matching to actual word iso part of a word
@inline_function_patterns.each do |user_format_string|
user_regex = Regexp.new(user_format_string)
cleanup_spaces_after_user_regex = /\s*/
inline_function_regex_formats << Regexp.new(user_regex.source + cleanup_spaces_after_user_regex.source)
word_boundary_before_user_regex = /\b/
cleanup_spaces_after_user_regex = /[ ]*\b/
inline_function_regex_formats << Regexp.new(word_boundary_before_user_regex.source + user_regex.source + cleanup_spaces_after_user_regex.source)
end
# let's clean up the encoding in case they've done anything weird with the characters we might find
source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil)
# - Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter
# Instead, look for "static inline" and parse it:
# - Everything before the match should just be copied, we don't want
# to touch anything but the inline functions.
# - Remove the implementation of the inline function (this is enclosed
# in square brackets) and replace it with ";" to complete the
# transformation to normal/non-inline function.
# To ensure proper removal of the function body, we count the number of square-bracket pairs
# and remove the pairs one-by-one.
# - Copy everything after the inline function implementation and start the parsing of the next inline function
# smush multiline macros into single line (checking for continuation character at end of line '\')
# If the user uses a macro to declare an inline function,
# smushing the macros makes it easier to recognize them as a macro and if required,
# remove them later on in this function
source.gsub!(/\s*\\\s*/m, ' ')
# Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter
# Instead, look for an inline pattern (f.e. "static inline") and parse it.
# Below is a small explanation on how the general mechanism works:
# - Everything before the match should just be copied, we don't want
# to touch anything but the inline functions.
# - Remove the implementation of the inline function (this is enclosed
# in square brackets) and replace it with ";" to complete the
# transformation to normal/non-inline function.
# To ensure proper removal of the function body, we count the number of square-bracket pairs
# and remove the pairs one-by-one.
# - Copy everything after the inline function implementation and start the parsing of the next inline function
# There are ofcourse some special cases (inline macro declarations, inline function declarations, ...) which are handled and explained below
inline_function_regex_formats.each do |format|
loop do
inline_function_match = source.match(/#{format}/) # Search for inline function declaration
break if nil == inline_function_match # No inline functions so nothing to do
# 1. Determine if we are dealing with a user defined macro to declare inline functions
# If the end of the pre-match string is a macro-declaration-like string,
# we are dealing with a user defined macro to declare inline functions
if /(#define\s*)\z/ === inline_function_match.pre_match
# Remove the macro from the source
stripped_pre_match = inline_function_match.pre_match.sub(/(#define\s*)\z/,'')
stripped_post_match = inline_function_match.post_match.sub(/\A(.*[\n]?)/,'')
source = stripped_pre_match + stripped_post_match
next
end
# 2. Determine if we are dealing with an inline function declaration iso function definition
# If the start of the post-match string is a function-declaration-like string (something ending with semicolon after the function arguments),
# we are dealing with a inline function declaration
if /\A#{@function_declaration_parse_base_match}\s*;/m === inline_function_match.post_match
# Only remove the inline part from the function declaration, leaving the function declaration won't do any harm
source = inline_function_match.pre_match + inline_function_match.post_match
next
end
# 3. If we get here, we found an inline function declaration AND inline function body.
# Remove the function body to transform it into a 'normal' function.
total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match)
break if 0 == total_pairs_to_remove # Bad source?