[修改] 增加freeRTOS
1. 版本FreeRTOSv202212.01,命名为kernel;
This commit is contained in:
@ -0,0 +1,111 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
['../config/production_environment',
|
||||
'cmock_header_parser',
|
||||
'cmock_generator',
|
||||
'cmock_file_writer',
|
||||
'cmock_config',
|
||||
'cmock_plugin_manager',
|
||||
'cmock_generator_utils',
|
||||
'cmock_unityhelper_parser'].each { |req| require "#{__dir__}/#{req}" }
|
||||
|
||||
class CMock
|
||||
def initialize(options = nil)
|
||||
cm_config = CMockConfig.new(options)
|
||||
cm_unityhelper = CMockUnityHelperParser.new(cm_config)
|
||||
cm_writer = CMockFileWriter.new(cm_config)
|
||||
cm_gen_utils = CMockGeneratorUtils.new(cm_config,
|
||||
:unity_helper => cm_unityhelper)
|
||||
cm_gen_plugins = CMockPluginManager.new(cm_config, cm_gen_utils)
|
||||
@cm_parser = CMockHeaderParser.new(cm_config)
|
||||
@cm_generator = CMockGenerator.new(cm_config, cm_writer, cm_gen_utils,
|
||||
cm_gen_plugins)
|
||||
@silent = (cm_config.verbosity < 2)
|
||||
end
|
||||
|
||||
def setup_mocks(files, folder = nil)
|
||||
[files].flatten.each do |src|
|
||||
generate_mock(src, folder)
|
||||
end
|
||||
end
|
||||
|
||||
def setup_skeletons(files)
|
||||
[files].flatten.each do |src|
|
||||
generate_skeleton src
|
||||
end
|
||||
end
|
||||
|
||||
private ###############################
|
||||
|
||||
def generate_mock(src, folder)
|
||||
name = File.basename(src, '.*')
|
||||
ext = File.extname(src)
|
||||
puts "Creating mock for #{name}..." unless @silent
|
||||
@cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)), ext, folder)
|
||||
end
|
||||
|
||||
def generate_skeleton(src)
|
||||
name = File.basename(src, '.*')
|
||||
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)
|
||||
options ||= {}
|
||||
options[key.to_sym] =
|
||||
if val.chr == ':'
|
||||
val[1..-1].to_sym
|
||||
elsif val.include? ';'
|
||||
val.split(';')
|
||||
elsif val == 'true'
|
||||
true
|
||||
elsif val == 'false'
|
||||
false
|
||||
elsif val =~ /^\d+$/
|
||||
val.to_i
|
||||
else
|
||||
val
|
||||
end
|
||||
options
|
||||
end
|
||||
|
||||
# Command Line Support ###############################
|
||||
|
||||
if $0 == __FILE__
|
||||
usage = "usage: ruby #{__FILE__} (-oOptionsFile) File(s)ToMock"
|
||||
|
||||
unless ARGV[0]
|
||||
puts usage
|
||||
exit 1
|
||||
end
|
||||
|
||||
options = {}
|
||||
filelist = []
|
||||
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 =~ /^--strippables=\"?(.*)\"?/
|
||||
# --strippables are dealt with separately since the user is allowed to
|
||||
# enter any valid regular expression as argument
|
||||
options = option_maker(options, 'strippables', Regexp.last_match(1))
|
||||
elsif arg =~ /^--([a-zA-Z0-9._\\\/:\s]+)=\"?([a-zA-Z0-9._\-\\\/:\s\;]*)\"?/x
|
||||
options = option_maker(options, Regexp.last_match(1),
|
||||
Regexp.last_match(2))
|
||||
else
|
||||
filelist << arg
|
||||
end
|
||||
end
|
||||
|
||||
if options[:skeleton]
|
||||
CMock.new(options).setup_skeletons(filelist)
|
||||
else
|
||||
CMock.new(options).setup_mocks(filelist)
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,174 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockConfig
|
||||
CMOCK_DEFAULT_OPTIONS =
|
||||
{
|
||||
:framework => :unity,
|
||||
:mock_path => 'mocks',
|
||||
:mock_prefix => 'Mock',
|
||||
:mock_suffix => '',
|
||||
:skeleton_path => '',
|
||||
:weak => '',
|
||||
:subdir => nil,
|
||||
:plugins => [],
|
||||
:strippables => ['(?:__attribute__\s*\(+.*?\)+)'],
|
||||
:attributes => %w[__ramfunc __irq __fiq register extern],
|
||||
:c_calling_conventions => %w[__stdcall __cdecl __fastcall],
|
||||
:enforce_strict_ordering => false,
|
||||
:fail_on_unexpected_calls => true,
|
||||
:unity_helper_path => false,
|
||||
:treat_as => {},
|
||||
:treat_as_array => {},
|
||||
:treat_as_void => [],
|
||||
:memcmp_if_unknown => true,
|
||||
:when_no_prototypes => :warn, # the options being :ignore, :warn, or :error
|
||||
:when_ptr => :compare_data, # the options being :compare_ptr, :compare_data, or :smart
|
||||
:verbosity => 2, # the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose
|
||||
:treat_externs => :exclude, # the options being :include or :exclude
|
||||
:treat_inlines => :exclude, # the options being :include or :exclude
|
||||
:callback_include_count => true,
|
||||
:callback_after_arg_check => false,
|
||||
:includes => nil,
|
||||
:includes_h_pre_orig_header => nil,
|
||||
:includes_h_post_orig_header => nil,
|
||||
:includes_c_pre_header => nil,
|
||||
:includes_c_post_header => nil,
|
||||
:orig_header_include_fmt => '#include "%s"',
|
||||
:array_size_type => [],
|
||||
:array_size_name => 'size|len',
|
||||
:skeleton => false,
|
||||
:exclude_setjmp_h => false,
|
||||
|
||||
# Format to look for inline functions.
|
||||
# This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static")
|
||||
# There are several possibilities:
|
||||
# - sometimes they appear together, sometimes individually,
|
||||
# - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff),
|
||||
# so we check for word boundaries when searching for them
|
||||
# - We first remove "static inline" combinations and boil down to single inline or static statements
|
||||
:inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*'] # Last part (\s*) is just to remove whitespaces (only to prettify the output)
|
||||
}.freeze
|
||||
|
||||
def initialize(options = nil)
|
||||
case options
|
||||
when NilClass then options = CMOCK_DEFAULT_OPTIONS.dup
|
||||
when String then options = CMOCK_DEFAULT_OPTIONS.dup.merge(load_config_file_from_yaml(options))
|
||||
when Hash then options = CMOCK_DEFAULT_OPTIONS.dup.merge(options)
|
||||
else raise 'If you specify arguments, it should be a filename or a hash of options'
|
||||
end
|
||||
|
||||
# do some quick type verification
|
||||
%i[plugins attributes treat_as_void].each do |opt|
|
||||
unless options[opt].class == Array
|
||||
options[opt] = []
|
||||
puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1
|
||||
end
|
||||
end
|
||||
%i[includes includes_h_pre_orig_header includes_h_post_orig_header includes_c_pre_header includes_c_post_header].each do |opt|
|
||||
unless options[opt].nil? || (options[opt].class == Array)
|
||||
options[opt] = []
|
||||
puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1
|
||||
end
|
||||
end
|
||||
options[:unity_helper_path] ||= options[:unity_helper]
|
||||
options[:unity_helper_path] = [options[:unity_helper_path]] if options[:unity_helper_path].is_a? String
|
||||
|
||||
if options[:unity_helper_path]
|
||||
require 'pathname'
|
||||
includes1 = options[:includes_c_post_header] || []
|
||||
includes2 = options[:unity_helper_path].map do |path|
|
||||
Pathname(path).relative_path_from(Pathname(options[:mock_path])).to_s
|
||||
end
|
||||
options[:includes_c_post_header] = (includes1 + includes2).uniq
|
||||
end
|
||||
|
||||
options[:plugins].compact!
|
||||
options[:plugins].map!(&:to_sym)
|
||||
@options = options
|
||||
|
||||
treat_as_map = standard_treat_as_map # .clone
|
||||
treat_as_map.merge!(@options[:treat_as])
|
||||
@options[:treat_as] = treat_as_map
|
||||
|
||||
@options.each_key do |key|
|
||||
unless methods.include?(key)
|
||||
eval("def #{key}() return @options[:#{key}] end")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load_config_file_from_yaml(yaml_filename)
|
||||
self.class.load_config_file_from_yaml yaml_filename
|
||||
end
|
||||
|
||||
def self.load_config_file_from_yaml(yaml_filename)
|
||||
require 'yaml'
|
||||
require 'fileutils'
|
||||
YAML.load_file(yaml_filename)[:cmock]
|
||||
end
|
||||
|
||||
def path(new_path)
|
||||
@src_path = new_path
|
||||
end
|
||||
|
||||
def load_unity_helper
|
||||
return nil unless @options[:unity_helper_path]
|
||||
|
||||
@options[:unity_helper_path].inject('') do |unity_helper, filename|
|
||||
unity_helper + "\n" + File.new(filename).read
|
||||
end
|
||||
end
|
||||
|
||||
def standard_treat_as_map
|
||||
{
|
||||
'int' => 'INT',
|
||||
'char' => 'INT8',
|
||||
'short' => 'INT16',
|
||||
'long' => 'INT',
|
||||
'int8' => 'INT8',
|
||||
'int16' => 'INT16',
|
||||
'int32' => 'INT',
|
||||
'int8_t' => 'INT8',
|
||||
'int16_t' => 'INT16',
|
||||
'int32_t' => 'INT',
|
||||
'INT8_T' => 'INT8',
|
||||
'INT16_T' => 'INT16',
|
||||
'INT32_T' => 'INT',
|
||||
'bool' => 'INT',
|
||||
'bool_t' => 'INT',
|
||||
'BOOL' => 'INT',
|
||||
'BOOL_T' => 'INT',
|
||||
'unsigned int' => 'HEX32',
|
||||
'unsigned long' => 'HEX32',
|
||||
'uint32' => 'HEX32',
|
||||
'uint32_t' => 'HEX32',
|
||||
'UINT32' => 'HEX32',
|
||||
'UINT32_T' => 'HEX32',
|
||||
'void*' => 'HEX8_ARRAY',
|
||||
'void const*' => 'HEX8_ARRAY',
|
||||
'const void*' => 'HEX8_ARRAY',
|
||||
'unsigned short' => 'HEX16',
|
||||
'uint16' => 'HEX16',
|
||||
'uint16_t' => 'HEX16',
|
||||
'UINT16' => 'HEX16',
|
||||
'UINT16_T' => 'HEX16',
|
||||
'unsigned char' => 'HEX8',
|
||||
'uint8' => 'HEX8',
|
||||
'uint8_t' => 'HEX8',
|
||||
'UINT8' => 'HEX8',
|
||||
'UINT8_T' => 'HEX8',
|
||||
'char*' => 'STRING',
|
||||
'char const*' => 'STRING',
|
||||
'const char*' => 'STRING',
|
||||
'pCHAR' => 'STRING',
|
||||
'cstring' => 'STRING',
|
||||
'CSTRING' => 'STRING',
|
||||
'float' => 'FLOAT',
|
||||
'double' => 'FLOAT'
|
||||
}
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,47 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockFileWriter
|
||||
attr_reader :config
|
||||
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def create_subdir(subdir)
|
||||
require 'fileutils'
|
||||
FileUtils.mkdir_p "#{@config.mock_path}/" unless Dir.exist?("#{@config.mock_path}/")
|
||||
FileUtils.mkdir_p "#{@config.mock_path}/#{subdir + '/' if subdir}" if subdir && !Dir.exist?("#{@config.mock_path}/#{subdir + '/' if subdir}")
|
||||
end
|
||||
|
||||
def create_file(filename, subdir)
|
||||
raise "Where's the block of data to create?" unless block_given?
|
||||
|
||||
full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new"
|
||||
full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}"
|
||||
File.open(full_file_name_temp, 'w') do |file|
|
||||
yield(file, filename)
|
||||
end
|
||||
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)
|
||||
require 'fileutils'
|
||||
FileUtils.rm(dest, :force => true)
|
||||
FileUtils.mv(src, dest)
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,368 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGenerator
|
||||
attr_accessor :config, :file_writer, :module_name, :module_ext, :clean_mock_name, :mock_name, :utils, :plugins, :weak, :ordered
|
||||
|
||||
def initialize(config, file_writer, utils, plugins)
|
||||
@file_writer = file_writer
|
||||
@utils = utils
|
||||
@plugins = plugins
|
||||
@config = config
|
||||
@prefix = @config.mock_prefix
|
||||
@suffix = @config.mock_suffix
|
||||
@weak = @config.weak
|
||||
@include_inline = @config.treat_inlines
|
||||
@ordered = @config.enforce_strict_ordering
|
||||
@framework = @config.framework.to_s
|
||||
@fail_on_unexpected_calls = @config.fail_on_unexpected_calls
|
||||
@exclude_setjmp_h = @config.exclude_setjmp_h
|
||||
@subdir = @config.subdir
|
||||
|
||||
@includes_h_pre_orig_header = (@config.includes || @config.includes_h_pre_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
|
||||
@includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
|
||||
@includes_c_pre_header = (@config.includes_c_pre_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
|
||||
@includes_c_post_header = (@config.includes_c_post_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
|
||||
|
||||
here = File.dirname __FILE__
|
||||
unity_path_in_ceedling = "#{here}/../../unity" # path to Unity from within Ceedling
|
||||
unity_path_in_cmock = "#{here}/../vendor/unity" # path to Unity from within CMock
|
||||
# path to Unity as specified by env var
|
||||
unity_path_in_env = ENV.key?('UNITY_DIR') ? File.expand_path(ENV.fetch('UNITY_DIR')) : nil
|
||||
|
||||
if unity_path_in_env && File.exist?(unity_path_in_env)
|
||||
require "#{unity_path_in_env}/auto/type_sanitizer"
|
||||
elsif File.exist? unity_path_in_ceedling
|
||||
require "#{unity_path_in_ceedling}/auto/type_sanitizer"
|
||||
elsif File.exist? unity_path_in_cmock
|
||||
require "#{unity_path_in_cmock}/auto/type_sanitizer"
|
||||
else
|
||||
raise 'Failed to find an instance of Unity to pull in type_sanitizer module!'
|
||||
end
|
||||
end
|
||||
|
||||
def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil)
|
||||
# determine the name for our new mock
|
||||
mock_name = @prefix + module_name + @suffix
|
||||
|
||||
# determine the folder our mock will reside
|
||||
mock_folder = if folder && @subdir
|
||||
File.join(@subdir, folder)
|
||||
elsif @subdir
|
||||
@subdir
|
||||
else
|
||||
folder
|
||||
end
|
||||
|
||||
# adds a trailing slash to the folder output
|
||||
mock_folder = File.join(mock_folder, '') if mock_folder
|
||||
|
||||
# create out mock project from incoming data
|
||||
mock_project = {
|
||||
:module_name => module_name,
|
||||
:module_ext => (module_ext || '.h'),
|
||||
:mock_name => mock_name,
|
||||
:clean_name => TypeSanitizer.sanitize_c_identifier(mock_name),
|
||||
:folder => mock_folder,
|
||||
:parsed_stuff => parsed_stuff,
|
||||
:skeleton => false
|
||||
}
|
||||
|
||||
create_mock_subdir(mock_project)
|
||||
create_mock_header_file(mock_project)
|
||||
create_mock_source_file(mock_project)
|
||||
end
|
||||
|
||||
def create_skeleton(module_name, parsed_stuff)
|
||||
mock_project = {
|
||||
:module_name => module_name,
|
||||
:module_ext => '.h',
|
||||
:parsed_stuff => parsed_stuff,
|
||||
:skeleton => true
|
||||
}
|
||||
|
||||
create_skeleton_source_file(mock_project)
|
||||
end
|
||||
|
||||
private if $ThisIsOnlyATest.nil? ##############################
|
||||
|
||||
def create_mock_subdir(mock_project)
|
||||
@file_writer.create_subdir(mock_project[:folder])
|
||||
end
|
||||
|
||||
def create_using_statement(file, function)
|
||||
file << "using namespace #{function[:namespace].join('::')};\n" unless function[:namespace].empty?
|
||||
end
|
||||
|
||||
def create_mock_header_file(mock_project)
|
||||
if @include_inline == :include
|
||||
@file_writer.create_file(mock_project[:module_name] + (mock_project[:module_ext]), mock_project[:folder]) do |file, _filename|
|
||||
file << mock_project[:parsed_stuff][:normalized_source]
|
||||
end
|
||||
end
|
||||
|
||||
@file_writer.create_file(mock_project[:mock_name] + mock_project[:module_ext], mock_project[:folder]) do |file, filename|
|
||||
create_mock_header_header(file, filename, mock_project)
|
||||
create_mock_header_service_call_declarations(file, mock_project)
|
||||
create_typedefs(file, mock_project)
|
||||
mock_project[:parsed_stuff][:functions].each do |function|
|
||||
create_using_statement(file, function)
|
||||
file << @plugins.run(:mock_function_declarations, function)
|
||||
end
|
||||
create_mock_header_footer(file)
|
||||
end
|
||||
end
|
||||
|
||||
def create_mock_source_file(mock_project)
|
||||
@file_writer.create_file(mock_project[:mock_name] + '.c', mock_project[:folder]) do |file, filename|
|
||||
create_source_header_section(file, filename, mock_project)
|
||||
create_instance_structure(file, mock_project)
|
||||
create_extern_declarations(file)
|
||||
create_mock_verify_function(file, mock_project)
|
||||
create_mock_init_function(file, mock_project)
|
||||
create_mock_destroy_function(file, mock_project)
|
||||
mock_project[:parsed_stuff][:functions].each do |function|
|
||||
create_mock_implementation(file, function)
|
||||
create_mock_interfaces(file, function)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_skeleton_source_file(mock_project)
|
||||
filename = "#{@config.mock_path}/#{@subdir + '/' if @subdir}#{mock_project[:module_name]}.c"
|
||||
existing = File.exist?(filename) ? File.read(filename) : ''
|
||||
@file_writer.append_file(mock_project[:module_name] + '.c', @subdir) do |file, fullname|
|
||||
blank_project = mock_project.clone
|
||||
blank_project[:parsed_stuff] = { :functions => [] }
|
||||
create_source_header_section(file, fullname, blank_project) if existing.empty?
|
||||
mock_project[:parsed_stuff][:functions].each do |function|
|
||||
create_function_skeleton(file, function, existing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_mock_header_header(file, _filename, mock_project)
|
||||
define_name = mock_project[:clean_name].upcase
|
||||
orig_filename = (mock_project[:folder] || '') + mock_project[:module_name] + mock_project[:module_ext]
|
||||
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n"
|
||||
file << "#ifndef _#{define_name}_H\n"
|
||||
file << "#define _#{define_name}_H\n\n"
|
||||
file << "#include \"#{@framework}.h\"\n"
|
||||
@includes_h_pre_orig_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << @config.orig_header_include_fmt.gsub(/%s/, orig_filename.to_s) + "\n"
|
||||
@includes_h_post_orig_header.each { |inc| file << "#include #{inc}\n" }
|
||||
plugin_includes = @plugins.run(:include_files)
|
||||
file << plugin_includes unless plugin_includes.empty?
|
||||
file << "\n"
|
||||
file << "/* Ignore the following warnings, since we are copying code */\n"
|
||||
file << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n"
|
||||
file << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n"
|
||||
file << "#pragma GCC diagnostic push\n"
|
||||
file << "#endif\n"
|
||||
file << "#if !defined(__clang__)\n"
|
||||
file << "#pragma GCC diagnostic ignored \"-Wpragmas\"\n"
|
||||
file << "#endif\n"
|
||||
file << "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n"
|
||||
file << "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n"
|
||||
file << "#endif\n"
|
||||
file << "\n"
|
||||
end
|
||||
|
||||
def create_typedefs(file, mock_project)
|
||||
file << "\n"
|
||||
mock_project[:parsed_stuff][:typedefs].each { |typedef| file << "#{typedef}\n" }
|
||||
file << "\n\n"
|
||||
end
|
||||
|
||||
def create_mock_header_service_call_declarations(file, mock_project)
|
||||
file << "void #{mock_project[:clean_name]}_Init(void);\n"
|
||||
file << "void #{mock_project[:clean_name]}_Destroy(void);\n"
|
||||
file << "void #{mock_project[:clean_name]}_Verify(void);\n\n"
|
||||
end
|
||||
|
||||
def create_mock_header_footer(header)
|
||||
header << "\n"
|
||||
header << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n"
|
||||
header << "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n"
|
||||
header << "#pragma GCC diagnostic pop\n"
|
||||
header << "#endif\n"
|
||||
header << "#endif\n"
|
||||
header << "\n"
|
||||
header << "#endif\n"
|
||||
end
|
||||
|
||||
def create_source_header_section(file, filename, mock_project)
|
||||
header_file = (mock_project[:folder] || '') + filename.gsub('.c', mock_project[:module_ext])
|
||||
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty?
|
||||
file << "#include <string.h>\n"
|
||||
file << "#include <stdlib.h>\n"
|
||||
unless @exclude_setjmp_h
|
||||
file << "#include <setjmp.h>\n"
|
||||
end
|
||||
file << "#include \"cmock.h\"\n"
|
||||
@includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "#include \"#{header_file}\"\n"
|
||||
@includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "\n"
|
||||
strs = []
|
||||
mock_project[:parsed_stuff][:functions].each do |func|
|
||||
strs << func[:name]
|
||||
func[:args].each { |arg| strs << arg[:name] }
|
||||
end
|
||||
strs.uniq.sort.each do |str|
|
||||
file << "static const char* CMockString_#{str} = \"#{str}\";\n"
|
||||
end
|
||||
file << "\n"
|
||||
end
|
||||
|
||||
def create_instance_structure(file, mock_project)
|
||||
functions = mock_project[:parsed_stuff][:functions]
|
||||
functions.each do |function|
|
||||
file << "typedef struct _CMOCK_#{function[:name]}_CALL_INSTANCE\n{\n"
|
||||
file << " UNITY_LINE_TYPE LineNumber;\n"
|
||||
file << @plugins.run(:instance_typedefs, function)
|
||||
file << "\n} CMOCK_#{function[:name]}_CALL_INSTANCE;\n\n"
|
||||
end
|
||||
file << "static struct #{mock_project[:clean_name]}Instance\n{\n"
|
||||
if functions.empty?
|
||||
file << " unsigned char placeHolder;\n"
|
||||
end
|
||||
functions.each do |function|
|
||||
file << @plugins.run(:instance_structure, function)
|
||||
file << " CMOCK_MEM_INDEX_TYPE #{function[:name]}_CallInstance;\n"
|
||||
end
|
||||
file << "} Mock;\n\n"
|
||||
end
|
||||
|
||||
def create_extern_declarations(file)
|
||||
unless @exclude_setjmp_h
|
||||
file << "extern jmp_buf AbortFrame;\n"
|
||||
end
|
||||
if @ordered
|
||||
file << "extern int GlobalExpectCount;\n"
|
||||
file << "extern int GlobalVerifyOrder;\n"
|
||||
end
|
||||
file << "\n"
|
||||
end
|
||||
|
||||
def create_mock_verify_function(file, mock_project)
|
||||
file << "void #{mock_project[:clean_name]}_Verify(void)\n{\n"
|
||||
verifications = mock_project[:parsed_stuff][:functions].collect do |function|
|
||||
v = @plugins.run(:mock_verify, function)
|
||||
v.empty? ? v : [" call_instance = Mock.#{function[:name]}_CallInstance;\n", v]
|
||||
end.join
|
||||
unless verifications.empty?
|
||||
file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n"
|
||||
file << " CMOCK_MEM_INDEX_TYPE call_instance;\n"
|
||||
file << verifications
|
||||
end
|
||||
file << "}\n\n"
|
||||
end
|
||||
|
||||
def create_mock_init_function(file, mock_project)
|
||||
file << "void #{mock_project[:clean_name]}_Init(void)\n{\n"
|
||||
file << " #{mock_project[:clean_name]}_Destroy();\n"
|
||||
file << "}\n\n"
|
||||
end
|
||||
|
||||
def create_mock_destroy_function(file, mock_project)
|
||||
file << "void #{mock_project[:clean_name]}_Destroy(void)\n{\n"
|
||||
file << " CMock_Guts_MemFreeAll();\n"
|
||||
file << " memset(&Mock, 0, sizeof(Mock));\n"
|
||||
file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_destroy, function) }.join
|
||||
|
||||
unless @fail_on_unexpected_calls
|
||||
file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_ignore, function) }.join
|
||||
end
|
||||
|
||||
if @ordered
|
||||
file << " GlobalExpectCount = 0;\n"
|
||||
file << " GlobalVerifyOrder = 0;\n"
|
||||
end
|
||||
file << "}\n\n"
|
||||
end
|
||||
|
||||
def create_mock_implementation(file, function)
|
||||
# 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?
|
||||
|
||||
# Encapsulate in namespace(s) if applicable
|
||||
function[:namespace].each do |ns|
|
||||
file << "namespace #{ns} {\n"
|
||||
end
|
||||
|
||||
# Determine class prefix (if any)
|
||||
cls_pre = ''
|
||||
unless function[:class].nil?
|
||||
cls_pre = "#{function[:class]}::"
|
||||
end
|
||||
|
||||
# Create mock function
|
||||
unless @weak.empty?
|
||||
file << "#if defined (__IAR_SYSTEMS_ICC__)\n"
|
||||
file << "#pragma weak #{function[:unscoped_name]}\n"
|
||||
file << "#else\n"
|
||||
file << "#{function_mod_and_rettype} #{function[:unscoped_name]}(#{args_string}) #{weak};\n"
|
||||
file << "#endif\n\n"
|
||||
end
|
||||
file << "#{function_mod_and_rettype} #{cls_pre}#{function[:unscoped_name]}(#{args_string})\n"
|
||||
file << "{\n"
|
||||
file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n"
|
||||
file << " CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance;\n"
|
||||
file << " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n"
|
||||
file << " cmock_call_instance = (CMOCK_#{function[:name]}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.#{function[:name]}_CallInstance);\n"
|
||||
file << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n"
|
||||
file << @plugins.run(:mock_implementation_precheck, function)
|
||||
file << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore);\n"
|
||||
file << " cmock_line = cmock_call_instance->LineNumber;\n"
|
||||
if @ordered
|
||||
file << " if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder)\n"
|
||||
file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly);\n"
|
||||
file << " if (cmock_call_instance->CallOrder < GlobalVerifyOrder)\n"
|
||||
file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledLate);\n"
|
||||
end
|
||||
file << @plugins.run(:mock_implementation, function)
|
||||
file << " UNITY_CLR_DETAILS();\n"
|
||||
file << " return cmock_call_instance->ReturnVal;\n" unless function[:return][:void?]
|
||||
file << "}\n"
|
||||
|
||||
# Close any namespace(s) opened above
|
||||
function[:namespace].each do
|
||||
file << "}\n"
|
||||
end
|
||||
|
||||
file << "\n"
|
||||
end
|
||||
|
||||
def create_mock_interfaces(file, function)
|
||||
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})"
|
||||
|
||||
return if 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
|
||||
@ -0,0 +1,63 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorPluginArray
|
||||
attr_reader :priority
|
||||
attr_accessor :config, :utils, :unity_helper, :ordered
|
||||
def initialize(config, utils)
|
||||
@config = config
|
||||
@ptr_handling = @config.when_ptr
|
||||
@ordered = @config.enforce_strict_ordering
|
||||
@utils = utils
|
||||
@unity_helper = @utils.helpers[:unity_helper]
|
||||
@priority = 8
|
||||
end
|
||||
|
||||
def instance_typedefs(function)
|
||||
function[:args].inject('') do |all, arg|
|
||||
arg[:ptr?] ? all + " int Expected_#{arg[:name]}_Depth;\n" : all
|
||||
end
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
return nil unless function[:contains_ptr?]
|
||||
|
||||
args_call = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : (m[:name]).to_s }.join(', ')
|
||||
args_string = function[:args].map do |m|
|
||||
type = @utils.arg_type_with_const(m)
|
||||
m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}"
|
||||
end.join(', ')
|
||||
if function[:return][:void?]
|
||||
return "#define #{function[:name]}_ExpectWithArray(#{args_call}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call})\n" \
|
||||
"void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string});\n"
|
||||
else
|
||||
return "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call}, cmock_retval)\n" \
|
||||
"void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]});\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
return nil unless function[:contains_ptr?]
|
||||
|
||||
lines = []
|
||||
func_name = function[:name]
|
||||
args_string = function[:args].map do |m|
|
||||
type = @utils.arg_type_with_const(m)
|
||||
m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}"
|
||||
end.join(', ')
|
||||
call_string = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : m[:name] }.join(', ')
|
||||
lines << if function[:return][:void?]
|
||||
"void #{func_name}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string})\n"
|
||||
else
|
||||
"void #{func_name}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]})\n"
|
||||
end
|
||||
lines << "{\n"
|
||||
lines << @utils.code_add_base_expectation(func_name)
|
||||
lines << " CMockExpectParameters_#{func_name}(cmock_call_instance, #{call_string});\n"
|
||||
lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" unless function[:return][:void?]
|
||||
lines << "}\n\n"
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,88 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorPluginCallback
|
||||
attr_accessor :include_count
|
||||
attr_reader :priority
|
||||
attr_reader :config, :utils
|
||||
|
||||
def initialize(config, utils)
|
||||
@config = config
|
||||
@utils = utils
|
||||
@priority = 6
|
||||
|
||||
@include_count = @config.callback_include_count
|
||||
end
|
||||
|
||||
def instance_structure(function)
|
||||
func_name = function[:name]
|
||||
" char #{func_name}_CallbackBool;\n" \
|
||||
" CMOCK_#{func_name}_CALLBACK #{func_name}_CallbackFunctionPointer;\n" \
|
||||
" int #{func_name}_CallbackCalls;\n"
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
func_name = function[:name]
|
||||
return_type = function[:return][:type]
|
||||
action = @config.callback_after_arg_check ? 'AddCallback' : 'Stub'
|
||||
style = (@include_count ? 1 : 0) | (function[:args].empty? ? 0 : 2)
|
||||
styles = ['void', 'int cmock_num_calls', function[:args_string], "#{function[:args_string]}, int cmock_num_calls"]
|
||||
"typedef #{return_type} (* CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \
|
||||
"void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback);\n" \
|
||||
"void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback);\n" \
|
||||
"#define #{func_name}_StubWithCallback #{func_name}_#{action}\n"
|
||||
end
|
||||
|
||||
def generate_call(function)
|
||||
args = function[:args].map { |m| m[:name] }
|
||||
args << "Mock.#{function[:name]}_CallbackCalls++" if @include_count
|
||||
"Mock.#{function[:name]}_CallbackFunctionPointer(#{args.join(', ')})"
|
||||
end
|
||||
|
||||
def mock_implementation(function)
|
||||
" if (Mock.#{function[:name]}_CallbackFunctionPointer != NULL)\n {\n" +
|
||||
if function[:return][:void?]
|
||||
" #{generate_call(function)};\n }\n"
|
||||
else
|
||||
" cmock_call_instance->ReturnVal = #{generate_call(function)};\n }\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_implementation_precheck(function)
|
||||
" if (!Mock.#{function[:name]}_CallbackBool &&\n" \
|
||||
" Mock.#{function[:name]}_CallbackFunctionPointer != NULL)\n {\n" +
|
||||
if function[:return][:void?]
|
||||
" #{generate_call(function)};\n" \
|
||||
" UNITY_CLR_DETAILS();\n" \
|
||||
" return;\n }\n"
|
||||
else
|
||||
" #{function[:return][:type]} cmock_cb_ret = #{generate_call(function)};\n" \
|
||||
" UNITY_CLR_DETAILS();\n" \
|
||||
" return cmock_cb_ret;\n }\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
func_name = function[:name]
|
||||
has_ignore = @config.plugins.include? :ignore
|
||||
lines = ''
|
||||
lines << "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback)\n{\n"
|
||||
lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore
|
||||
lines << " Mock.#{func_name}_CallbackBool = (char)1;\n"
|
||||
lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n"
|
||||
lines << "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback)\n{\n"
|
||||
lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore
|
||||
lines << " Mock.#{func_name}_CallbackBool = (char)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
|
||||
@ -0,0 +1,50 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorPluginCexception
|
||||
attr_reader :priority
|
||||
attr_reader :config, :utils
|
||||
|
||||
def initialize(config, utils)
|
||||
@config = config
|
||||
@utils = utils
|
||||
@priority = 7
|
||||
raise 'Error: cexception is not supported without setjmp support' if @config.exclude_setjmp_h
|
||||
end
|
||||
|
||||
def include_files
|
||||
"#include \"CException.h\"\n"
|
||||
end
|
||||
|
||||
def instance_typedefs(_function)
|
||||
" CEXCEPTION_T ExceptionToThrow;\n"
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
if function[:args_string] == 'void'
|
||||
"#define #{function[:name]}_ExpectAndThrow(cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, cmock_to_throw)\n" \
|
||||
"void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, CEXCEPTION_T cmock_to_throw);\n"
|
||||
else
|
||||
"#define #{function[:name]}_ExpectAndThrow(#{function[:args_call]}, cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, #{function[:args_call]}, cmock_to_throw)\n" \
|
||||
"void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, CEXCEPTION_T cmock_to_throw);\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_implementation(_function)
|
||||
" if (cmock_call_instance->ExceptionToThrow != CEXCEPTION_NONE)\n {\n" \
|
||||
" UNITY_CLR_DETAILS();\n" \
|
||||
" Throw(cmock_call_instance->ExceptionToThrow);\n }\n"
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
arg_insert = function[:args_string] == 'void' ? '' : "#{function[:args_string]}, "
|
||||
["void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{arg_insert}CEXCEPTION_T cmock_to_throw)\n{\n",
|
||||
@utils.code_add_base_expectation(function[:name]),
|
||||
@utils.code_call_argument_loader(function),
|
||||
" cmock_call_instance->ExceptionToThrow = cmock_to_throw;\n",
|
||||
"}\n\n"].join
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,100 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorPluginExpect
|
||||
attr_reader :priority
|
||||
attr_accessor :config, :utils, :unity_helper, :ordered
|
||||
|
||||
def initialize(config, utils)
|
||||
@config = config
|
||||
@ptr_handling = @config.when_ptr
|
||||
@ordered = @config.enforce_strict_ordering
|
||||
@utils = utils
|
||||
@unity_helper = @utils.helpers[:unity_helper]
|
||||
@priority = 5
|
||||
|
||||
if @config.plugins.include? :expect_any_args
|
||||
alias :mock_implementation :mock_implementation_might_check_args
|
||||
else
|
||||
alias :mock_implementation :mock_implementation_always_check_args
|
||||
end
|
||||
end
|
||||
|
||||
def instance_typedefs(function)
|
||||
lines = ''
|
||||
lines << " #{function[:return][:type]} ReturnVal;\n" unless function[:return][:void?]
|
||||
lines << " int CallOrder;\n" if @ordered
|
||||
function[:args].each do |arg|
|
||||
lines << " #{arg[:type]} Expected_#{arg[:name]};\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
if function[:args].empty?
|
||||
if function[:return][:void?]
|
||||
"#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n" \
|
||||
"void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n"
|
||||
else
|
||||
"#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n" \
|
||||
"void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
|
||||
end
|
||||
elsif function[:return][:void?]
|
||||
"#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n" \
|
||||
"void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n"
|
||||
else
|
||||
"#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n" \
|
||||
"void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_implementation_always_check_args(function)
|
||||
lines = ''
|
||||
function[:args].each do |arg|
|
||||
lines << @utils.code_verify_an_arg_expectation(function, arg)
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_implementation_might_check_args(function)
|
||||
return '' if function[:args].empty?
|
||||
|
||||
lines = " if (!cmock_call_instance->ExpectAnyArgsBool)\n {\n"
|
||||
function[:args].each do |arg|
|
||||
lines << @utils.code_verify_an_arg_expectation(function, arg)
|
||||
end
|
||||
lines << " }\n"
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
lines = ''
|
||||
func_name = function[:name]
|
||||
lines << if function[:return][:void?]
|
||||
if function[:args_string] == 'void'
|
||||
"void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line)\n{\n"
|
||||
else
|
||||
"void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]})\n{\n"
|
||||
end
|
||||
elsif function[:args_string] == 'void'
|
||||
"void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n"
|
||||
else
|
||||
"void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n"
|
||||
end
|
||||
lines << @utils.code_add_base_expectation(func_name)
|
||||
lines << @utils.code_call_argument_loader(function)
|
||||
lines << @utils.code_assign_argument_quickly('cmock_call_instance->ReturnVal', function[:return]) unless function[:return][:void?]
|
||||
lines << "}\n\n"
|
||||
end
|
||||
|
||||
def mock_verify(function)
|
||||
" if (CMOCK_GUTS_NONE != call_instance)\n" \
|
||||
" {\n" \
|
||||
" UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" \
|
||||
" UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess);\n" \
|
||||
" }\n"
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,50 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorPluginExpectAnyArgs
|
||||
attr_reader :priority
|
||||
attr_reader :config, :utils
|
||||
|
||||
def initialize(config, utils)
|
||||
@config = config
|
||||
@utils = utils
|
||||
@priority = 3
|
||||
end
|
||||
|
||||
def instance_typedefs(_function)
|
||||
" char ExpectAnyArgsBool;\n"
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
if function[:args].empty?
|
||||
''
|
||||
elsif function[:return][:void?]
|
||||
"#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" \
|
||||
"void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n"
|
||||
else
|
||||
"#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n" \
|
||||
"void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
lines = ''
|
||||
unless function[:args].empty?
|
||||
lines << if function[:return][:void?]
|
||||
"void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line)\n{\n"
|
||||
else
|
||||
"void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n"
|
||||
end
|
||||
lines << @utils.code_add_base_expectation(function[:name], true)
|
||||
unless function[:return][:void?]
|
||||
lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n"
|
||||
end
|
||||
lines << " cmock_call_instance->ExpectAnyArgsBool = (char)1;\n"
|
||||
lines << "}\n\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,88 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorPluginIgnore
|
||||
attr_reader :priority
|
||||
attr_reader :config, :utils
|
||||
|
||||
def initialize(config, utils)
|
||||
@config = config
|
||||
@utils = utils
|
||||
@priority = 2
|
||||
end
|
||||
|
||||
def instance_structure(function)
|
||||
if function[:return][:void?]
|
||||
" char #{function[:name]}_IgnoreBool;\n"
|
||||
else
|
||||
" char #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
lines = if function[:return][:void?]
|
||||
"#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \
|
||||
"void #{function[:name]}_CMockIgnore(void);\n"
|
||||
else
|
||||
"#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n" \
|
||||
"void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
|
||||
end
|
||||
|
||||
# Add stop ignore function. it does not matter if there are any args
|
||||
lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \
|
||||
"void #{function[:name]}_CMockStopIgnore(void);\n"
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_implementation_precheck(function)
|
||||
lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n"
|
||||
lines << " UNITY_CLR_DETAILS();\n"
|
||||
if function[:return][:void?]
|
||||
lines << " return;\n }\n"
|
||||
else
|
||||
retval = function[:return].merge(:name => 'cmock_call_instance->ReturnVal')
|
||||
lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n"
|
||||
lines << ' ' + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless retval[:void?]
|
||||
lines << " return cmock_call_instance->ReturnVal;\n }\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
lines = ''
|
||||
lines << if function[:return][:void?]
|
||||
"void #{function[:name]}_CMockIgnore(void)\n{\n"
|
||||
else
|
||||
"void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n"
|
||||
end
|
||||
unless function[:return][:void?]
|
||||
lines << @utils.code_add_base_expectation(function[:name], false)
|
||||
end
|
||||
unless function[:return][:void?]
|
||||
lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n"
|
||||
end
|
||||
lines << " Mock.#{function[:name]}_IgnoreBool = (char)1;\n"
|
||||
lines << "}\n\n"
|
||||
|
||||
# Add stop ignore function. it does not matter if there are any args
|
||||
lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n"
|
||||
unless function[:return][:void?]
|
||||
lines << " if(Mock.#{function[:name]}_IgnoreBool)\n"
|
||||
lines << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n"
|
||||
end
|
||||
lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n"
|
||||
lines << "}\n\n"
|
||||
end
|
||||
|
||||
def mock_ignore(function)
|
||||
" Mock.#{function[:name]}_IgnoreBool = (char) 1;\n"
|
||||
end
|
||||
|
||||
def mock_verify(function)
|
||||
func_name = function[:name]
|
||||
" if (Mock.#{func_name}_IgnoreBool)\n call_instance = CMOCK_GUTS_NONE;\n"
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,42 @@
|
||||
class CMockGeneratorPluginIgnoreArg
|
||||
attr_reader :priority
|
||||
attr_accessor :utils
|
||||
|
||||
def initialize(_config, utils)
|
||||
@utils = utils
|
||||
@priority = 10
|
||||
end
|
||||
|
||||
def instance_typedefs(function)
|
||||
lines = ''
|
||||
function[:args].each do |arg|
|
||||
lines << " char IgnoreArg_#{arg[:name]};\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
lines = ''
|
||||
function[:args].each do |arg|
|
||||
lines << "#define #{function[:name]}_IgnoreArg_#{arg[:name]}()"
|
||||
lines << " #{function[:name]}_CMockIgnoreArg_#{arg[:name]}(__LINE__)\n"
|
||||
lines << "void #{function[:name]}_CMockIgnoreArg_#{arg[:name]}(UNITY_LINE_TYPE cmock_line);\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
lines = []
|
||||
func_name = function[:name]
|
||||
function[:args].each do |arg|
|
||||
lines << "void #{func_name}_CMockIgnoreArg_#{arg[:name]}(UNITY_LINE_TYPE cmock_line)\n"
|
||||
lines << "{\n"
|
||||
lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \
|
||||
"(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n"
|
||||
lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp);\n"
|
||||
lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 1;\n"
|
||||
lines << "}\n\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,85 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorPluginIgnoreStateless
|
||||
attr_reader :priority
|
||||
attr_reader :config, :utils
|
||||
|
||||
def initialize(config, utils)
|
||||
@config = config
|
||||
@utils = utils
|
||||
@priority = 2
|
||||
end
|
||||
|
||||
def instance_structure(function)
|
||||
if function[:return][:void?]
|
||||
" char #{function[:name]}_IgnoreBool;\n"
|
||||
else
|
||||
" char #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n"
|
||||
end
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
lines = if function[:return][:void?]
|
||||
"#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \
|
||||
"void #{function[:name]}_CMockIgnore(void);\n"
|
||||
else
|
||||
"#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(cmock_retval)\n" \
|
||||
"void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]});\n"
|
||||
end
|
||||
|
||||
# Add stop ignore function. it does not matter if there are any args
|
||||
lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \
|
||||
"void #{function[:name]}_CMockStopIgnore(void);\n"
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_implementation_precheck(function)
|
||||
lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n"
|
||||
lines << " UNITY_CLR_DETAILS();\n"
|
||||
if function[:return][:void?]
|
||||
lines << " return;\n }\n"
|
||||
else
|
||||
retval = function[:return].merge(:name => 'cmock_call_instance->ReturnVal')
|
||||
lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n"
|
||||
lines << ' ' + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless retval[:void?]
|
||||
lines << " return cmock_call_instance->ReturnVal;\n }\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
# this function is adjusted
|
||||
def mock_interfaces(function)
|
||||
lines = ''
|
||||
lines << if function[:return][:void?]
|
||||
"void #{function[:name]}_CMockIgnore(void)\n{\n"
|
||||
else
|
||||
"void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]})\n{\n"
|
||||
end
|
||||
unless function[:return][:void?]
|
||||
lines << " Mock.#{function[:name]}_CallInstance = CMOCK_GUTS_NONE;\n"
|
||||
lines << " Mock.#{function[:name]}_FinalReturn = cmock_to_return;\n"
|
||||
end
|
||||
lines << " Mock.#{function[:name]}_IgnoreBool = (char)1;\n"
|
||||
lines << "}\n\n"
|
||||
|
||||
# Add stop ignore function. it does not matter if there are any args
|
||||
lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n"
|
||||
lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n"
|
||||
lines << "}\n\n"
|
||||
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_ignore(function)
|
||||
" Mock.#{function[:name]}_IgnoreBool = (char)1;\n"
|
||||
end
|
||||
|
||||
def mock_verify(function)
|
||||
func_name = function[:name]
|
||||
" if (Mock.#{func_name}_IgnoreBool)\n call_instance = CMOCK_GUTS_NONE;\n"
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,79 @@
|
||||
class CMockGeneratorPluginReturnThruPtr
|
||||
attr_reader :priority
|
||||
attr_accessor :utils
|
||||
|
||||
def initialize(_config, utils)
|
||||
@utils = utils
|
||||
@priority = 9
|
||||
end
|
||||
|
||||
def instance_typedefs(function)
|
||||
lines = ''
|
||||
function[:args].each do |arg|
|
||||
next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
|
||||
|
||||
lines << " char ReturnThruPtr_#{arg[:name]}_Used;\n"
|
||||
lines << " #{arg[:type]} ReturnThruPtr_#{arg[:name]}_Val;\n"
|
||||
lines << " size_t ReturnThruPtr_#{arg[:name]}_Size;\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_function_declarations(function)
|
||||
lines = ''
|
||||
function[:args].each do |arg|
|
||||
next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
|
||||
|
||||
lines << "#define #{function[:name]}_ReturnThruPtr_#{arg[:name]}(#{arg[:name]})"
|
||||
# If the pointer type actually contains an asterisk, we can do sizeof the type (super safe), otherwise
|
||||
# we need to do a sizeof the dereferenced pointer (which could be a problem if give the wrong size
|
||||
lines << if arg[:type][-1] == '*'
|
||||
" #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(#{arg[:type][0..-2]}))\n"
|
||||
else
|
||||
" #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n"
|
||||
end
|
||||
lines << "#define #{function[:name]}_ReturnArrayThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_len)"
|
||||
lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_len * sizeof(*#{arg[:name]}))\n"
|
||||
lines << "#define #{function[:name]}_ReturnMemThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_size)"
|
||||
lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_size)\n"
|
||||
lines << "void #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg[:name]}, size_t cmock_size);\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_interfaces(function)
|
||||
lines = []
|
||||
func_name = function[:name]
|
||||
function[:args].each do |arg|
|
||||
arg_name = arg[:name]
|
||||
next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
|
||||
|
||||
lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg_name}, size_t cmock_size)\n"
|
||||
lines << "{\n"
|
||||
lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \
|
||||
"(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n"
|
||||
lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp);\n"
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used = 1;\n"
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Val = #{arg_name};\n"
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size = cmock_size;\n"
|
||||
lines << "}\n\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def mock_implementation(function)
|
||||
lines = []
|
||||
function[:args].each do |arg|
|
||||
arg_name = arg[:name]
|
||||
next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
|
||||
|
||||
lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n"
|
||||
lines << " {\n"
|
||||
lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n"
|
||||
lines << " memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n"
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n"
|
||||
lines << " }\n"
|
||||
end
|
||||
lines
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,250 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockGeneratorUtils
|
||||
attr_accessor :config, :helpers, :ordered, :ptr_handling, :arrays, :cexception
|
||||
|
||||
def initialize(config, helpers = {})
|
||||
@config = config
|
||||
@ptr_handling = @config.when_ptr
|
||||
@ordered = @config.enforce_strict_ordering
|
||||
@arrays = @config.plugins.include? :array
|
||||
@cexception = @config.plugins.include? :cexception
|
||||
@expect_any = @config.plugins.include? :expect_any_args
|
||||
@return_thru_ptr = @config.plugins.include? :return_thru_ptr
|
||||
@ignore_arg = @config.plugins.include? :ignore_arg
|
||||
@ignore = @config.plugins.include? :ignore
|
||||
@ignore_stateless = @config.plugins.include? :ignore_stateless
|
||||
@treat_as = @config.treat_as
|
||||
@helpers = helpers
|
||||
end
|
||||
|
||||
def self.arg_type_with_const(arg)
|
||||
# Restore any "const" that was removed in header parsing
|
||||
if arg[:type].include?('*')
|
||||
arg[:const_ptr?] ? "#{arg[:type]} const" : arg[:type]
|
||||
else
|
||||
arg[:const?] ? "const #{arg[:type]}" : arg[:type]
|
||||
end
|
||||
end
|
||||
|
||||
def arg_type_with_const(arg)
|
||||
self.class.arg_type_with_const(arg)
|
||||
end
|
||||
|
||||
def code_verify_an_arg_expectation(function, arg)
|
||||
if @arrays
|
||||
case @ptr_handling
|
||||
when :smart then code_verify_an_arg_expectation_with_smart_arrays(function, arg)
|
||||
when :compare_data then code_verify_an_arg_expectation_with_normal_arrays(function, arg)
|
||||
when :compare_ptr then raise "ERROR: the array plugin doesn't enjoy working with :compare_ptr only. Disable one option."
|
||||
end
|
||||
else
|
||||
code_verify_an_arg_expectation_with_no_arrays(function, arg)
|
||||
end
|
||||
end
|
||||
|
||||
def code_add_base_expectation(func_name, global_ordering_supported = true)
|
||||
lines = " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n"
|
||||
lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n"
|
||||
lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n"
|
||||
lines << " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n"
|
||||
lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n"
|
||||
lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if @ignore || @ignore_stateless
|
||||
lines << " cmock_call_instance->LineNumber = cmock_line;\n"
|
||||
lines << " cmock_call_instance->CallOrder = ++GlobalExpectCount;\n" if @ordered && global_ordering_supported
|
||||
lines << " cmock_call_instance->ExceptionToThrow = CEXCEPTION_NONE;\n" if @cexception
|
||||
lines << " cmock_call_instance->ExpectAnyArgsBool = (char)0;\n" if @expect_any
|
||||
lines
|
||||
end
|
||||
|
||||
def code_add_an_arg_expectation(arg, depth = 1)
|
||||
lines = code_assign_argument_quickly("cmock_call_instance->Expected_#{arg[:name]}", arg)
|
||||
lines << " cmock_call_instance->Expected_#{arg[:name]}_Depth = #{arg[:name]}_Depth;\n" if @arrays && (depth.class == String)
|
||||
lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 0;\n" if @ignore_arg
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg[:name]}_Used = 0;\n" if @return_thru_ptr && ptr_or_str?(arg[:type]) && !(arg[:const?])
|
||||
lines
|
||||
end
|
||||
|
||||
def code_assign_argument_quickly(dest, arg)
|
||||
if arg[:ptr?] || @treat_as.include?(arg[:type])
|
||||
" #{dest} = #{arg[:name]};\n"
|
||||
else
|
||||
assert_expr = "sizeof(#{arg[:name]}) == sizeof(#{arg[:type]}) ? 1 : -1"
|
||||
comment = "/* add #{arg[:type]} to :treat_as_array if this causes an error */"
|
||||
" memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" \
|
||||
" sizeof(#{arg[:type]}[#{assert_expr}])); #{comment}\n"
|
||||
end
|
||||
end
|
||||
|
||||
def code_add_argument_loader(function)
|
||||
if function[:args_string] != 'void'
|
||||
if @arrays
|
||||
args_string = function[:args].map do |m|
|
||||
type = arg_type_with_const(m)
|
||||
m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}"
|
||||
end.join(', ')
|
||||
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string});\n" \
|
||||
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\n" +
|
||||
function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg, (arg[:ptr?] ? "#{arg[:name]}_Depth" : 1)) } +
|
||||
"}\n\n"
|
||||
else
|
||||
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]});\n" \
|
||||
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]})\n{\n" +
|
||||
function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg) } +
|
||||
"}\n\n"
|
||||
end
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
def code_call_argument_loader(function)
|
||||
if function[:args_string] != 'void'
|
||||
args = function[:args].map do |m|
|
||||
if @arrays && m[:ptr?] && !(m[:array_data?])
|
||||
"#{m[:name]}, 1"
|
||||
elsif @arrays && m[:array_size?]
|
||||
"#{m[:name]}, #{m[:name]}"
|
||||
else
|
||||
m[:name]
|
||||
end
|
||||
end
|
||||
" CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n"
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
def ptr_or_str?(arg_type)
|
||||
(arg_type.include?('*') ||
|
||||
@treat_as.fetch(arg_type, '').include?('*'))
|
||||
end
|
||||
|
||||
# private ######################
|
||||
|
||||
def lookup_expect_type(_function, arg)
|
||||
c_type = arg[:type]
|
||||
arg_name = arg[:name]
|
||||
expected = "cmock_call_instance->Expected_#{arg_name}"
|
||||
ignore = "cmock_call_instance->IgnoreArg_#{arg_name}"
|
||||
unity_func = if (arg[:ptr?]) && ((c_type =~ /\*\*/) || (@ptr_handling == :compare_ptr))
|
||||
['UNITY_TEST_ASSERT_EQUAL_PTR', '']
|
||||
else
|
||||
@helpers.nil? || @helpers[:unity_helper].nil? ? ['UNITY_TEST_ASSERT_EQUAL', ''] : @helpers[:unity_helper].get_helper(c_type)
|
||||
end
|
||||
[c_type, arg_name, expected, ignore, unity_func[0], unity_func[1]]
|
||||
end
|
||||
|
||||
def code_verify_an_arg_expectation_with_no_arrays(function, arg)
|
||||
c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg)
|
||||
lines = ''
|
||||
lines << " if (!#{ignore})\n" if @ignore_arg
|
||||
lines << " {\n"
|
||||
lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n"
|
||||
case unity_func
|
||||
when 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
|
||||
c_type_local = c_type.gsub(/\*$/, '')
|
||||
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n"
|
||||
when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY'
|
||||
if pre == '&'
|
||||
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n"
|
||||
else
|
||||
lines << " if (#{pre}#{expected} == NULL)\n"
|
||||
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
|
||||
lines << " else\n"
|
||||
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch); }\n"
|
||||
end
|
||||
when /_ARRAY/
|
||||
if pre == '&'
|
||||
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch);\n"
|
||||
else
|
||||
lines << " if (#{pre}#{expected} == NULL)\n"
|
||||
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
|
||||
lines << " else\n"
|
||||
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch); }\n"
|
||||
end
|
||||
else
|
||||
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n"
|
||||
end
|
||||
lines << " }\n"
|
||||
lines
|
||||
end
|
||||
|
||||
def code_verify_an_arg_expectation_with_normal_arrays(function, arg)
|
||||
c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg)
|
||||
depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1
|
||||
lines = ''
|
||||
lines << " if (!#{ignore})\n" if @ignore_arg
|
||||
lines << " {\n"
|
||||
lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n"
|
||||
case unity_func
|
||||
when 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
|
||||
c_type_local = c_type.gsub(/\*$/, '')
|
||||
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n"
|
||||
when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY'
|
||||
if pre == '&'
|
||||
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n"
|
||||
else
|
||||
lines << " if (#{pre}#{expected} == NULL)\n"
|
||||
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
|
||||
lines << " else\n"
|
||||
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n"
|
||||
end
|
||||
when /_ARRAY/
|
||||
if pre == '&'
|
||||
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n"
|
||||
else
|
||||
lines << " if (#{pre}#{expected} == NULL)\n"
|
||||
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
|
||||
lines << " else\n"
|
||||
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n"
|
||||
end
|
||||
else
|
||||
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n"
|
||||
end
|
||||
lines << " }\n"
|
||||
lines
|
||||
end
|
||||
|
||||
def code_verify_an_arg_expectation_with_smart_arrays(function, arg)
|
||||
c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg)
|
||||
depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1
|
||||
lines = ''
|
||||
lines << " if (!#{ignore})\n" if @ignore_arg
|
||||
lines << " {\n"
|
||||
lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n"
|
||||
case unity_func
|
||||
when 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
|
||||
c_type_local = c_type.gsub(/\*$/, '')
|
||||
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n"
|
||||
when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY'
|
||||
if pre == '&'
|
||||
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch);\n"
|
||||
else
|
||||
lines << " if (#{pre}#{expected} == NULL)\n"
|
||||
lines << " { UNITY_TEST_ASSERT_NULL(#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
|
||||
lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '')
|
||||
lines << " else\n"
|
||||
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n"
|
||||
end
|
||||
when /_ARRAY/
|
||||
if pre == '&'
|
||||
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n"
|
||||
else
|
||||
lines << " if (#{pre}#{expected} == NULL)\n"
|
||||
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
|
||||
lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '')
|
||||
lines << " else\n"
|
||||
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n"
|
||||
end
|
||||
else
|
||||
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n"
|
||||
end
|
||||
lines << " }\n"
|
||||
lines
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,623 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockHeaderParser
|
||||
attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns
|
||||
|
||||
def initialize(cfg)
|
||||
@c_strippables = cfg.strippables
|
||||
@c_attr_noconst = cfg.attributes.uniq - ['const']
|
||||
@c_attributes = ['const'] + c_attr_noconst
|
||||
@c_calling_conventions = cfg.c_calling_conventions.uniq
|
||||
@treat_as_array = cfg.treat_as_array
|
||||
@treat_as_void = (['void'] + cfg.treat_as_void).uniq
|
||||
@function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+\-\/]*)\)'
|
||||
@declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m
|
||||
@standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq
|
||||
@array_size_name = cfg.array_size_name
|
||||
@array_size_type = (%w[int size_t] + cfg.array_size_type).uniq
|
||||
@when_no_prototypes = cfg.when_no_prototypes
|
||||
@local_as_void = @treat_as_void
|
||||
@verbosity = cfg.verbosity
|
||||
@treat_externs = cfg.treat_externs
|
||||
@treat_inlines = cfg.treat_inlines
|
||||
@inline_function_patterns = cfg.inline_function_patterns
|
||||
@c_strippables += ['extern'] if @treat_externs == :include # we'll need to remove the attribute if we're allowing externs
|
||||
@c_strippables += ['inline'] if @treat_inlines == :include # we'll need to remove the attribute if we're allowing inlines
|
||||
end
|
||||
|
||||
def parse(name, source)
|
||||
parse_project = {
|
||||
:module_name => name.gsub(/\W/, ''),
|
||||
:typedefs => [],
|
||||
:functions => [],
|
||||
:normalized_source => nil
|
||||
}
|
||||
|
||||
function_names = []
|
||||
|
||||
all_funcs = parse_functions(import_source(source, parse_project)).map { |item| [item] }
|
||||
all_funcs += parse_cpp_functions(import_source(source, parse_project, true))
|
||||
all_funcs.map do |decl|
|
||||
func = parse_declaration(parse_project, *decl)
|
||||
unless function_names.include? func[:name]
|
||||
parse_project[:functions] << func
|
||||
function_names << func[:name]
|
||||
end
|
||||
end
|
||||
|
||||
parse_project[:normalized_source] = if @treat_inlines == :include
|
||||
transform_inline_functions(source)
|
||||
else
|
||||
''
|
||||
end
|
||||
|
||||
{ :includes => nil,
|
||||
:functions => parse_project[:functions],
|
||||
:typedefs => parse_project[:typedefs],
|
||||
:normalized_source => parse_project[:normalized_source] }
|
||||
end
|
||||
|
||||
private if $ThisIsOnlyATest.nil? ################
|
||||
|
||||
# Remove C/C++ comments from a string
|
||||
# +source+:: String which will have the comments removed
|
||||
def remove_comments_from_source(source)
|
||||
# remove comments (block and line, in three steps to ensure correct precedence)
|
||||
source.gsub!(/(?<!\*)\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks
|
||||
source.gsub!(/\/\*.*?\*\//m, '') # remove block comments
|
||||
source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain)
|
||||
end
|
||||
|
||||
def remove_nested_pairs_of_braces(source)
|
||||
# remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection)
|
||||
if RUBY_VERSION.split('.')[0].to_i > 1
|
||||
# we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash.
|
||||
r = '\\{([^\\{\\}]*|\\g<0>)*\\}'
|
||||
source.gsub!(/#{r}/m, '{ }')
|
||||
else
|
||||
while source.gsub!(/\{[^\{\}]*\{[^\{\}]*\}[^\{\}]*\}/m, '{ }')
|
||||
end
|
||||
end
|
||||
|
||||
source
|
||||
end
|
||||
|
||||
# Return the number of pairs of braces/square brackets in the function provided by the user
|
||||
# +source+:: String containing the function to be processed
|
||||
def count_number_of_pairs_of_braces_in_function(source)
|
||||
is_function_start_found = false
|
||||
curr_level = 0
|
||||
total_pairs = 0
|
||||
|
||||
source.each_char do |c|
|
||||
if c == '{'
|
||||
curr_level += 1
|
||||
total_pairs += 1
|
||||
is_function_start_found = true
|
||||
elsif c == '}'
|
||||
curr_level -= 1
|
||||
end
|
||||
|
||||
break if is_function_start_found && curr_level == 0 # We reached the end of the inline function body
|
||||
end
|
||||
|
||||
if curr_level != 0
|
||||
total_pairs = 0 # Something is fishy about this source, not enough closing braces?
|
||||
end
|
||||
|
||||
total_pairs
|
||||
end
|
||||
|
||||
# Transform inline functions to regular functions in the source by the user
|
||||
# +source+:: String containing the source to be processed
|
||||
def transform_inline_functions(source)
|
||||
inline_function_regex_formats = []
|
||||
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)
|
||||
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)
|
||||
|
||||
# Comments can contain words that will trigger the parser (static|inline|<user_defined_static_keyword>)
|
||||
remove_comments_from_source(source)
|
||||
|
||||
# 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|
|
||||
inspected_source = ''
|
||||
regex_matched = false
|
||||
loop do
|
||||
inline_function_match = source.match(/#{format}/) # Search for inline function declaration
|
||||
|
||||
if inline_function_match.nil? # No inline functions so nothing to do
|
||||
# Join pre and post match stripped parts for the next inline function detection regex
|
||||
source = inspected_source + source if regex_matched == true
|
||||
break
|
||||
end
|
||||
|
||||
regex_matched = true
|
||||
# 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]?)/, '')
|
||||
inspected_source += stripped_pre_match
|
||||
source = 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
|
||||
inspected_source += inline_function_match.pre_match
|
||||
source = 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 declaration.
|
||||
if /\A#{@function_declaration_parse_base_match}\s*\{/m =~ inline_function_match.post_match
|
||||
total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match)
|
||||
|
||||
break if total_pairs_to_remove == 0 # Bad source?
|
||||
|
||||
inline_function_stripped = inline_function_match.post_match
|
||||
|
||||
total_pairs_to_remove.times do
|
||||
inline_function_stripped.sub!(/\s*#{square_bracket_pair_regex_format}/, ';') # Remove inline implementation (+ some whitespace because it's prettier)
|
||||
end
|
||||
inspected_source += inline_function_match.pre_match
|
||||
source = inline_function_stripped
|
||||
next
|
||||
end
|
||||
|
||||
# 4. If we get here, it means the regex match, but it is not related to the function (ex. static variable in header)
|
||||
# Leave this code as it is.
|
||||
inspected_source += inline_function_match.pre_match + inline_function_match[0]
|
||||
source = inline_function_match.post_match
|
||||
end
|
||||
end
|
||||
|
||||
source
|
||||
end
|
||||
|
||||
def import_source(source, parse_project, cpp = false)
|
||||
# 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)
|
||||
|
||||
# void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void
|
||||
# to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void
|
||||
@local_as_void = @treat_as_void
|
||||
void_types = source.scan(/typedef\s+(?:\(\s*)?void(?:\s*\))?\s+([\w]+)\s*;/)
|
||||
if void_types
|
||||
@local_as_void += void_types.flatten.uniq.compact
|
||||
end
|
||||
|
||||
# If user wants to mock inline functions,
|
||||
# remove the (user specific) inline keywords before removing anything else to avoid missing an inline function
|
||||
if @treat_inlines == :include
|
||||
@inline_function_patterns.each do |user_format_string|
|
||||
source.gsub!(/#{user_format_string}/, '') # remove user defined inline function patterns
|
||||
end
|
||||
end
|
||||
|
||||
# smush multiline macros into single line (checking for continuation character at end of line '\')
|
||||
source.gsub!(/\s*\\\s*/m, ' ')
|
||||
|
||||
remove_comments_from_source(source)
|
||||
|
||||
# remove assembler pragma sections
|
||||
source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '')
|
||||
|
||||
# remove gcc's __attribute__ tags
|
||||
source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '')
|
||||
|
||||
# remove preprocessor statements and extern "C"
|
||||
source.gsub!(/^\s*#.*/, '')
|
||||
source.gsub!(/extern\s+\"C\"\s*\{/, '')
|
||||
|
||||
# enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them
|
||||
# forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes
|
||||
source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs
|
||||
source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces
|
||||
# remove problem keywords
|
||||
source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2')
|
||||
source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp
|
||||
|
||||
source.gsub!(/\s*=\s*['"a-zA-Z0-9_\.]+\s*/, '') # remove default value statements from argument lists
|
||||
source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements
|
||||
source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric
|
||||
source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/, '\1') unless @c_strippables.empty? # remove known attributes slated to be stripped
|
||||
|
||||
# scan standalone function pointers and remove them, because they can just be ignored
|
||||
source.gsub!(/\w+\s*\(\s*\*\s*\w+\s*\)\s*\([^)]*\)\s*;/, ';')
|
||||
|
||||
# scan for functions which return function pointers, because they are a pain
|
||||
source.gsub!(/([\w\s\*]+)\(*\(\s*\*([\w\s\*]+)\s*\(([\w\s\*,]*)\)\)\s*\(([\w\s\*,]*)\)\)*/) do |_m|
|
||||
functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"
|
||||
unless cpp # only collect once
|
||||
parse_project[:typedefs] << "typedef #{Regexp.last_match(1).strip}(*#{functype})(#{Regexp.last_match(4)});"
|
||||
"#{functype} #{Regexp.last_match(2).strip}(#{Regexp.last_match(3)});"
|
||||
end
|
||||
end
|
||||
|
||||
source = remove_nested_pairs_of_braces(source) unless cpp
|
||||
|
||||
if @treat_inlines == :include
|
||||
# Functions having "{ }" at this point are/were inline functions,
|
||||
# User wants them in so 'disguise' them as normal functions with the ";"
|
||||
source.gsub!('{ }', ';')
|
||||
end
|
||||
|
||||
# remove function definitions by stripping off the arguments right now
|
||||
source.gsub!(/\([^\)]*\)\s*\{[^\}]*\}/m, ';')
|
||||
|
||||
# drop extra white space to make the rest go faster
|
||||
source.gsub!(/^\s+/, '') # remove extra white space from beginning of line
|
||||
source.gsub!(/\s+$/, '') # remove extra white space from end of line
|
||||
source.gsub!(/\s*\(\s*/, '(') # remove extra white space from before left parens
|
||||
source.gsub!(/\s*\)\s*/, ')') # remove extra white space from before right parens
|
||||
source.gsub!(/\s+/, ' ') # remove remaining extra white space
|
||||
|
||||
# split lines on semicolons and remove things that are obviously not what we are looking for
|
||||
src_lines = source.split(/\s*;\s*/)
|
||||
src_lines = src_lines.uniq unless cpp # must retain closing braces for class/namespace
|
||||
src_lines.delete_if { |line| line.strip.empty? } # remove blank lines
|
||||
src_lines.delete_if { |line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil? } # remove function pointer arrays
|
||||
|
||||
unless @treat_externs == :include
|
||||
src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:extern)\s+/).nil? } # remove extern functions
|
||||
end
|
||||
|
||||
unless @treat_inlines == :include
|
||||
src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil? } # remove inline functions
|
||||
end
|
||||
|
||||
src_lines.delete_if(&:empty?) # drop empty lines
|
||||
end
|
||||
|
||||
# Rudimentary C++ parser - does not handle all situations - e.g.:
|
||||
# * A namespace function appears after a class with private members (should be parsed)
|
||||
# * Anonymous namespace (shouldn't parse anything - no matter how nested - within it)
|
||||
# * A class nested within another class
|
||||
def parse_cpp_functions(source)
|
||||
funcs = []
|
||||
|
||||
ns = []
|
||||
pub = false
|
||||
source.each do |line|
|
||||
# Search for namespace, class, opening and closing braces
|
||||
line.scan(/(?:(?:\b(?:namespace|class)\s+(?:\S+)\s*)?{)|}/).each do |item|
|
||||
if item == '}'
|
||||
ns.pop
|
||||
else
|
||||
token = item.strip.sub(/\s+/, ' ')
|
||||
ns << token
|
||||
|
||||
pub = false if token.start_with? 'class'
|
||||
pub = true if token.start_with? 'namespace'
|
||||
end
|
||||
end
|
||||
|
||||
pub = true if line =~ /public:/
|
||||
pub = false if line =~ /private:/ || line =~ /protected:/
|
||||
|
||||
# ignore non-public and non-static
|
||||
next unless pub
|
||||
next unless line =~ /\bstatic\b/
|
||||
|
||||
line.sub!(/^.*static/, '')
|
||||
next unless line =~ @declaration_parse_matcher
|
||||
|
||||
tmp = ns.reject { |item| item == '{' }
|
||||
|
||||
# Identify class name, if any
|
||||
cls = nil
|
||||
if tmp[-1].start_with? 'class '
|
||||
cls = tmp.pop.sub(/class (\S+) {/, '\1')
|
||||
end
|
||||
|
||||
# Assemble list of namespaces
|
||||
tmp.each { |item| item.sub!(/(?:namespace|class) (\S+) {/, '\1') }
|
||||
|
||||
funcs << [line.strip.gsub(/\s+/, ' '), tmp, cls]
|
||||
end
|
||||
funcs
|
||||
end
|
||||
|
||||
def parse_functions(source)
|
||||
funcs = []
|
||||
source.each { |line| funcs << line.strip.gsub(/\s+/, ' ') if line =~ @declaration_parse_matcher }
|
||||
if funcs.empty?
|
||||
case @when_no_prototypes
|
||||
when :error
|
||||
raise 'ERROR: No function prototypes found!'
|
||||
when :warn
|
||||
puts 'WARNING: No function prototypes found!' unless @verbosity < 1
|
||||
end
|
||||
end
|
||||
funcs
|
||||
end
|
||||
|
||||
def parse_type_and_name(arg)
|
||||
# Split up words and remove known attributes. For pointer types, make sure
|
||||
# to remove 'const' only when it applies to the pointer itself, not when it
|
||||
# applies to the type pointed to. For non-pointer types, remove any
|
||||
# occurrence of 'const'.
|
||||
arg.gsub!(/(\w)\*/, '\1 *') # pull asterisks away from preceding word
|
||||
arg.gsub!(/\*(\w)/, '* \1') # pull asterisks away from following word
|
||||
arg_array = arg.split
|
||||
arg_info = divine_ptr_and_const(arg)
|
||||
arg_info[:name] = arg_array[-1]
|
||||
|
||||
attributes = arg.include?('*') ? @c_attr_noconst : @c_attributes
|
||||
attr_array = []
|
||||
type_array = []
|
||||
|
||||
arg_array[0..-2].each do |word|
|
||||
if attributes.include?(word)
|
||||
attr_array << word
|
||||
elsif @c_calling_conventions.include?(word)
|
||||
arg_info[:c_calling_convention] = word
|
||||
else
|
||||
type_array << word
|
||||
end
|
||||
end
|
||||
|
||||
if arg_info[:const_ptr?]
|
||||
attr_array << 'const'
|
||||
type_array.delete_at(type_array.rindex('const'))
|
||||
end
|
||||
|
||||
arg_info[:modifier] = attr_array.join(' ')
|
||||
arg_info[:type] = type_array.join(' ').gsub(/\s+\*/, '*') # remove space before asterisks
|
||||
arg_info
|
||||
end
|
||||
|
||||
def parse_args(arg_list)
|
||||
args = []
|
||||
arg_list.split(',').each do |arg|
|
||||
arg.strip!
|
||||
return args if arg =~ /^\s*((\.\.\.)|(void))\s*$/ # we're done if we reach void by itself or ...
|
||||
|
||||
arg_info = parse_type_and_name(arg)
|
||||
arg_info.delete(:modifier) # don't care about this
|
||||
arg_info.delete(:c_calling_convention) # don't care about this
|
||||
|
||||
# in C, array arguments implicitly degrade to pointers
|
||||
# make the translation explicit here to simplify later logic
|
||||
if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?])
|
||||
arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*"
|
||||
arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?]
|
||||
arg_info[:ptr?] = true
|
||||
end
|
||||
|
||||
args << arg_info
|
||||
end
|
||||
|
||||
# Try to find array pair in parameters following this pattern : <type> * <name>, <@array_size_type> <@array_size_name>
|
||||
args.each_with_index do |val, index|
|
||||
next_index = index + 1
|
||||
next unless args.length > next_index
|
||||
|
||||
if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type])
|
||||
val[:array_data?] = true
|
||||
args[next_index][:array_size?] = true
|
||||
end
|
||||
end
|
||||
|
||||
args
|
||||
end
|
||||
|
||||
def divine_ptr(arg)
|
||||
return false unless arg.include? '*'
|
||||
# treat "const char *" and similar as a string, not a pointer
|
||||
return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def divine_const(arg)
|
||||
# a non-pointer arg containing "const" is a constant
|
||||
# an arg containing "const" before the last * is a pointer to a constant
|
||||
if arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) : (/(^|\s)const(\s|$)/ =~ arg)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def divine_ptr_and_const(arg)
|
||||
divination = {}
|
||||
|
||||
divination[:ptr?] = divine_ptr(arg)
|
||||
divination[:const?] = divine_const(arg)
|
||||
|
||||
# an arg containing "const" after the last * is a constant pointer
|
||||
divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false
|
||||
|
||||
divination
|
||||
end
|
||||
|
||||
def clean_args(arg_list, parse_project)
|
||||
if @local_as_void.include?(arg_list.strip) || arg_list.empty?
|
||||
'void'
|
||||
else
|
||||
c = 0
|
||||
# magically turn brackets into asterisks, also match for parentheses that come from macros
|
||||
arg_list.gsub!(/(\w+)(?:\s*\[[^\[\]]*\])+/, '*\1')
|
||||
# remove space to place asterisks with type (where they belong)
|
||||
arg_list.gsub!(/\s+\*/, '*')
|
||||
# pull asterisks away from arg to place asterisks with type (where they belong)
|
||||
arg_list.gsub!(/\*(\w)/, '* \1')
|
||||
|
||||
# scan argument list for function pointers and replace them with custom types
|
||||
arg_list.gsub!(/([\w\s\*]+)\(+\s*\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m|
|
||||
functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"
|
||||
funcret = Regexp.last_match(1).strip
|
||||
funcname = Regexp.last_match(2).strip
|
||||
funcargs = Regexp.last_match(3).strip
|
||||
funconst = ''
|
||||
if funcname.include? 'const'
|
||||
funcname.gsub!('const', '').strip!
|
||||
funconst = 'const '
|
||||
end
|
||||
parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});"
|
||||
funcname = "cmock_arg#{c += 1}" if funcname.empty?
|
||||
"#{functype} #{funconst}#{funcname}"
|
||||
end
|
||||
|
||||
# scan argument list for function pointers with shorthand notation and replace them with custom types
|
||||
arg_list.gsub!(/([\w\s\*]+)+\s+(\w+)\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m|
|
||||
functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"
|
||||
funcret = Regexp.last_match(1).strip
|
||||
funcname = Regexp.last_match(2).strip
|
||||
funcargs = Regexp.last_match(3).strip
|
||||
funconst = ''
|
||||
if funcname.include? 'const'
|
||||
funcname.gsub!('const', '').strip!
|
||||
funconst = 'const '
|
||||
end
|
||||
parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});"
|
||||
funcname = "cmock_arg#{c += 1}" if funcname.empty?
|
||||
"#{functype} #{funconst}#{funcname}"
|
||||
end
|
||||
|
||||
# automatically name unnamed arguments (those that only had a type)
|
||||
arg_list.split(/\s*,\s*/).map do |arg|
|
||||
parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*'])
|
||||
if (parts.size < 2) || (parts[-1][-1].chr == '*') || @standards.include?(parts[-1])
|
||||
"#{arg} cmock_arg#{c += 1}"
|
||||
else
|
||||
arg
|
||||
end
|
||||
end.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
def parse_declaration(parse_project, declaration, namespace = [], classname = nil)
|
||||
decl = {}
|
||||
decl[:namespace] = namespace
|
||||
decl[:class] = classname
|
||||
|
||||
regex_match = @declaration_parse_matcher.match(declaration)
|
||||
raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil?
|
||||
|
||||
# grab argument list
|
||||
args = regex_match[2].strip
|
||||
|
||||
# process function attributes, return type, and name
|
||||
parsed = parse_type_and_name(regex_match[1])
|
||||
|
||||
# Record original name without scope prefix
|
||||
decl[:unscoped_name] = parsed[:name]
|
||||
|
||||
# Prefix name with namespace scope (if any) and then class
|
||||
decl[:name] = namespace.join('_')
|
||||
unless classname.nil?
|
||||
decl[:name] << '_' unless decl[:name].empty?
|
||||
decl[:name] << classname
|
||||
end
|
||||
# Add original name to complete fully scoped name
|
||||
decl[:name] << '_' unless decl[:name].empty?
|
||||
decl[:name] << decl[:unscoped_name]
|
||||
|
||||
decl[:modifier] = parsed[:modifier]
|
||||
unless parsed[:c_calling_convention].nil?
|
||||
decl[:c_calling_convention] = parsed[:c_calling_convention]
|
||||
end
|
||||
|
||||
rettype = parsed[:type]
|
||||
rettype = 'void' if @local_as_void.include?(rettype.strip)
|
||||
decl[:return] = { :type => rettype,
|
||||
:name => 'cmock_to_return',
|
||||
:str => "#{rettype} cmock_to_return",
|
||||
:void? => (rettype == 'void'),
|
||||
:ptr? => parsed[:ptr?] || false,
|
||||
:const? => parsed[:const?] || false,
|
||||
:const_ptr? => parsed[:const_ptr?] || false }
|
||||
|
||||
# remove default argument statements from mock definitions
|
||||
args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ')
|
||||
|
||||
# check for var args
|
||||
if args =~ /\.\.\./
|
||||
decl[:var_arg] = args.match(/[\w\s]*\.\.\./).to_s.strip
|
||||
args = if args =~ /\,[\w\s]*\.\.\./
|
||||
args.gsub!(/\,[\w\s]*\.\.\./, '')
|
||||
else
|
||||
'void'
|
||||
end
|
||||
else
|
||||
decl[:var_arg] = nil
|
||||
end
|
||||
args = clean_args(args, parse_project)
|
||||
decl[:args_string] = args
|
||||
decl[:args] = parse_args(args)
|
||||
decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ')
|
||||
decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr }
|
||||
|
||||
if decl[:return][:type].nil? || decl[:name].nil? || decl[:args].nil? ||
|
||||
decl[:return][:type].empty? || decl[:name].empty?
|
||||
raise "Failed Parsing Declaration Prototype!\n" \
|
||||
" declaration: '#{declaration}'\n" \
|
||||
" modifier: '#{decl[:modifier]}'\n" \
|
||||
" return: #{prototype_inspect_hash(decl[:return])}\n" \
|
||||
" function: '#{decl[:name]}'\n" \
|
||||
" args: #{prototype_inspect_array_of_hashes(decl[:args])}\n"
|
||||
end
|
||||
|
||||
decl
|
||||
end
|
||||
|
||||
def prototype_inspect_hash(hash)
|
||||
pairs = []
|
||||
hash.each_pair { |name, value| pairs << ":#{name} => #{"'" if value.class == String}#{value}#{"'" if value.class == String}" }
|
||||
"{#{pairs.join(', ')}}"
|
||||
end
|
||||
|
||||
def prototype_inspect_array_of_hashes(array)
|
||||
hashes = []
|
||||
array.each { |hash| hashes << prototype_inspect_hash(hash) }
|
||||
case array.size
|
||||
when 0
|
||||
return '[]'
|
||||
when 1
|
||||
return "[#{hashes[0]}]"
|
||||
else
|
||||
return "[\n #{hashes.join("\n ")}\n ]\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,50 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockPluginManager
|
||||
attr_accessor :plugins
|
||||
|
||||
def initialize(config, utils)
|
||||
@plugins = []
|
||||
plugins_to_load = [:expect, config.plugins].flatten.uniq.compact
|
||||
plugins_to_load.each do |plugin|
|
||||
plugin_name = plugin.to_s
|
||||
object_name = 'CMockGeneratorPlugin' + camelize(plugin_name)
|
||||
self.class.mutex.synchronize { load_plugin(plugin_name, object_name, config, utils) }
|
||||
end
|
||||
@plugins.sort! { |a, b| a.priority <=> b.priority }
|
||||
end
|
||||
|
||||
def run(method, args = nil)
|
||||
if args.nil?
|
||||
@plugins.collect { |plugin| plugin.send(method) if plugin.respond_to?(method) }.flatten.join
|
||||
else
|
||||
@plugins.collect { |plugin| plugin.send(method, args) if plugin.respond_to?(method) }.flatten.join
|
||||
end
|
||||
end
|
||||
|
||||
def camelize(lower_case_and_underscored_word)
|
||||
lower_case_and_underscored_word.gsub(/\/(.?)/) { '::' + Regexp.last_match(1).upcase }.gsub(/(^|_)(.)/) { Regexp.last_match(2).upcase }
|
||||
end
|
||||
|
||||
def self.mutex
|
||||
@mutex ||= Mutex.new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_plugin(plugin_name, object_name, config, utils)
|
||||
unless Object.const_defined? object_name
|
||||
file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb"
|
||||
require file_name
|
||||
end
|
||||
class_name = Object.const_get(object_name)
|
||||
@plugins << class_name.new(config, utils)
|
||||
rescue StandardError
|
||||
file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb"
|
||||
raise "ERROR: CMock unable to load plugin '#{plugin_name}' '#{object_name}' #{file_name}"
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,77 @@
|
||||
# ==========================================
|
||||
# CMock Project - Automatic Mock Generation for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
class CMockUnityHelperParser
|
||||
attr_accessor :c_types
|
||||
|
||||
def initialize(config)
|
||||
@config = config
|
||||
@fallback = @config.plugins.include?(:array) ? 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' : 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
|
||||
@c_types = map_c_types.merge(import_source)
|
||||
end
|
||||
|
||||
def get_helper(ctype)
|
||||
lookup = ctype.gsub(/(?:^|(\S?)(\s*)|(\W))const(?:$|(\s*)(\S)|(\W))/, '\1\3\5\6').strip.gsub(/\s+/, '_')
|
||||
return [@c_types[lookup], ''] if @c_types[lookup]
|
||||
|
||||
if lookup =~ /\*$/
|
||||
lookup = lookup.gsub(/\*$/, '')
|
||||
return [@c_types[lookup], '*'] if @c_types[lookup]
|
||||
else
|
||||
lookup += '*'
|
||||
return [@c_types[lookup], '&'] if @c_types[lookup]
|
||||
end
|
||||
return ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] if ctype =~ /cmock_\w+_ptr\d+/
|
||||
raise("Don't know how to test #{ctype} and memory tests are disabled!") unless @config.memcmp_if_unknown
|
||||
|
||||
lookup =~ /\*$/ ? [@fallback, '&'] : [@fallback, '']
|
||||
end
|
||||
|
||||
private ###########################
|
||||
|
||||
def map_c_types
|
||||
c_types = {}
|
||||
@config.treat_as.each_pair do |ctype, expecttype|
|
||||
c_type = ctype.gsub(/\s+/, '_')
|
||||
if expecttype =~ /\*/
|
||||
c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype.delete('*')}_ARRAY"
|
||||
else
|
||||
c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype}"
|
||||
c_types[c_type + '*'] ||= "UNITY_TEST_ASSERT_EQUAL_#{expecttype}_ARRAY"
|
||||
end
|
||||
end
|
||||
c_types
|
||||
end
|
||||
|
||||
def import_source
|
||||
source = @config.load_unity_helper
|
||||
return {} if source.nil?
|
||||
|
||||
c_types = {}
|
||||
source = source.gsub(/\/\/.*$/, '') # remove line comments
|
||||
source = source.gsub(/\/\*.*?\*\//m, '') # remove block comments
|
||||
|
||||
# scan for comparison helpers
|
||||
match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+))\s*\(' + Array.new(4, '\s*\w+\s*').join(',') + '\)')
|
||||
pairs = source.scan(match_regex).flatten.compact
|
||||
(pairs.size / 2).times do |i|
|
||||
expect = pairs[i * 2]
|
||||
ctype = pairs[(i * 2) + 1]
|
||||
c_types[ctype] = expect unless expect.include?('_ARRAY')
|
||||
end
|
||||
|
||||
# scan for array variants of those helpers
|
||||
match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+_ARRAY))\s*\(' + Array.new(5, '\s*\w+\s*').join(',') + '\)')
|
||||
pairs = source.scan(match_regex).flatten.compact
|
||||
(pairs.size / 2).times do |i|
|
||||
expect = pairs[i * 2]
|
||||
ctype = pairs[(i * 2) + 1]
|
||||
c_types[ctype.gsub('_ARRAY', '*')] = expect
|
||||
end
|
||||
|
||||
c_types
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user