mirror of
https://github.com/ThrowTheSwitch/CMock.git
synced 2026-06-05 21:15:20 +00:00
git-svn-id: http://cmock.svn.sourceforge.net/svnroot/cmock/trunk@5 bf332499-1b4d-0410-844d-d2d48d5cc64c
This commit is contained in:
+4
-15
@@ -1,15 +1,4 @@
|
||||
|
||||
class CMock
|
||||
attr_accessor :mocks_path, :includes, :interface_parser
|
||||
|
||||
def initialize(mocks_path='mocks', includes=[], interface_parser=nil)
|
||||
@mocks_path = mocks_path
|
||||
@includes = includes
|
||||
@interface_parser = interface_parser
|
||||
end
|
||||
|
||||
def generate(module_header)
|
||||
@interface_parser.extract_interface(module_header)
|
||||
end
|
||||
|
||||
end
|
||||
$here = File.dirname __FILE__
|
||||
require "#{$here}/cmock_setup"
|
||||
require "#{$here}/cmock_generator"
|
||||
require "#{$here}/c_file_parser"
|
||||
|
||||
@@ -0,0 +1,545 @@
|
||||
$here = File.dirname __FILE__
|
||||
require "#{$here}/c_file_parser"
|
||||
|
||||
class CMockGenerator
|
||||
|
||||
attr_accessor :module_name, :src_path, :mock_path, :tab, :includes
|
||||
|
||||
require 'ftools'
|
||||
|
||||
def initialize(module_name=nil, src_path='src', mock_path='mocks', includes=[], use_cexception=true, allow_ignore_mock=false, tab=' ')
|
||||
raise "Must at least specify a code module" if module_name.nil?
|
||||
@module_name = module_name
|
||||
@src_path = src_path
|
||||
@mock_path = mock_path
|
||||
@tab = tab
|
||||
@throw_type = 'int'
|
||||
@call_count_type = 'unsigned short'
|
||||
@ignore_bool = 'unsigned char'
|
||||
@includes = includes
|
||||
@use_cexception = use_cexception
|
||||
@allow_ignore_mock = allow_ignore_mock
|
||||
end
|
||||
|
||||
def create_mock
|
||||
update_vars
|
||||
create_mock_header_file
|
||||
create_mock_source_file
|
||||
end
|
||||
|
||||
def update_vars
|
||||
@parser = CFileParser.new(File.read("#{@src_path}/#{@module_name}.h"))
|
||||
@mock_name = "Mock" + @module_name
|
||||
@mock_header_name_dest = @mock_name + ".h"
|
||||
@mock_imp_name_dest = @mock_name + ".c"
|
||||
@mock_header_temp = @mock_name + ".h.new"
|
||||
@mock_imp_temp = @mock_name + ".c.new"
|
||||
@mock_header_file_path = "#{@mock_path}/#{@mock_header_name_dest}"
|
||||
@mock_imp_file_path = "#{@mock_path}/#{@mock_imp_name_dest}"
|
||||
end
|
||||
|
||||
def update_file(dest, src)
|
||||
File.delete(dest) if (File.exist?(dest))
|
||||
File.copy(src, dest)
|
||||
File.delete(src)
|
||||
end
|
||||
|
||||
def create_mock_header_file
|
||||
File.open @mock_header_temp, 'w' do |header|
|
||||
@header = header
|
||||
create_mock_header_header(@mock_header_name_dest.gsub(/\.h/, "_h").upcase)
|
||||
create_mock_header_externs
|
||||
@parser.nondefine_functions.each do |function|
|
||||
create_mock_header_function_declaration(function)
|
||||
end
|
||||
create_mock_header_footer
|
||||
end
|
||||
update_file @mock_header_file_path, @mock_header_temp
|
||||
end
|
||||
|
||||
def create_mock_source_file
|
||||
File.open @mock_imp_temp, 'w' do |implementation|
|
||||
@source = implementation
|
||||
create_source_header_section
|
||||
create_instance_structure
|
||||
create_extern_declarations
|
||||
create_mock_verify_function
|
||||
create_mock_init_function
|
||||
create_mock_destroy_function
|
||||
@parser.nondefine_functions.each do |function|
|
||||
create_mock_implementation(function)
|
||||
create_mock_function_expectation(function)
|
||||
create_mock_function_expectation_with_throw(function) if (@use_cexception)
|
||||
create_mock_function_ignore(function) if (@allow_ignore_mock)
|
||||
end
|
||||
end
|
||||
update_file @mock_imp_file_path, @mock_imp_temp
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_mock_header_header(define_name)
|
||||
@header << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n"
|
||||
@header << "#ifndef _#{define_name}\n"
|
||||
@header << "#define _#{define_name}\n\n"
|
||||
@header << "#include \"#{@mock_header_name_dest.gsub(/^Mock/, "")}\"\n\n"
|
||||
@header << "void #{@mock_name}_Init(void);\n"
|
||||
@header << "void #{@mock_name}_Destroy(void);\n"
|
||||
@header << "void #{@mock_name}_Verify(void);\n\n"
|
||||
end
|
||||
|
||||
def create_mock_header_externs
|
||||
@parser.externs.each do |extern|
|
||||
@header << extern << ";\n"
|
||||
end
|
||||
@header << "\n" unless @parser.externs.empty?
|
||||
end
|
||||
|
||||
def create_mock_header_function_declaration(function)
|
||||
decl = parse_declaration(function)
|
||||
|
||||
if decl[:args_no_var_args] == "void"
|
||||
if decl[:return] == "void"
|
||||
@header << "void #{decl[:function]}_Expect(void);\n"
|
||||
else
|
||||
@header << "void #{decl[:function]}_ExpectAndReturn(#{decl[:return]} toReturn);\n"
|
||||
end
|
||||
else
|
||||
if decl[:return] == "void"
|
||||
@header << "void #{decl[:function]}_Expect(#{decl[:args_no_var_args]});\n"
|
||||
else
|
||||
@header << "void #{decl[:function]}_ExpectAndReturn(#{decl[:args_no_var_args]}, #{decl[:return]} toReturn);\n"
|
||||
end
|
||||
end
|
||||
|
||||
if (@use_cexception)
|
||||
if decl[:args_no_var_args] == "void"
|
||||
@header << "void #{decl[:function]}_ExpectAndThrow(int toThrow);\n"
|
||||
else
|
||||
@header << "void #{decl[:function]}_ExpectAndThrow(#{decl[:args_no_var_args]}, int toThrow);\n"
|
||||
end
|
||||
end
|
||||
|
||||
if (@allow_ignore_mock)
|
||||
if decl[:return] == "void"
|
||||
@header << "void #{decl[:function]}_Ignore(void);\n"
|
||||
else
|
||||
@header << "void #{decl[:function]}_IgnoreAndReturn(#{decl[:return]} toReturn);\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_mock_header_footer
|
||||
@header << "\n#endif\n"
|
||||
end
|
||||
|
||||
def create_source_header_section
|
||||
@source << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n"
|
||||
@source << "#include <string.h>\n"
|
||||
@source << "#include <stdlib.h>\n"
|
||||
@source << "#include <setjmp.h>\n"
|
||||
@source << "#include \"unity.h\"\n"
|
||||
@source << "#include \"Exception.h\"\n" if (@use_cexception)
|
||||
@includes.each {|include| @source << "#include \"#{include}\"\n"}
|
||||
@source << "#include \"#{@mock_header_name_dest}\"\n\n"
|
||||
end
|
||||
|
||||
def create_instance_structure
|
||||
@source << "static struct #{@mock_name}Instance\n"
|
||||
@source << "{\n"
|
||||
|
||||
if @parser.nondefine_functions.length == 0
|
||||
@source << "#{@tab}unsigned char placeHolder;\n"
|
||||
end
|
||||
|
||||
@source << "#{@tab}unsigned char allocFailure;\n"
|
||||
|
||||
@parser.nondefine_functions.each do |function|
|
||||
decl = parse_declaration(function)
|
||||
|
||||
@source << "#{@tab}#{@call_count_type} #{decl[:function]}_CallCount;\n"
|
||||
@source << "#{@tab}#{@call_count_type} #{decl[:function]}_CallsExpected;\n"
|
||||
|
||||
if (@allow_ignore_mock)
|
||||
@source << "#{@tab}#{@ignore_bool} #{decl[:function]}_IgnoreBool;\n"
|
||||
end
|
||||
|
||||
@source << "#{@tab}#{@call_count_type} *#{decl[:function]}_ThrowOnCallCount;\n"
|
||||
@source << "#{@tab}#{@call_count_type} *#{decl[:function]}_ThrowOnCallCount_Head;\n"
|
||||
@source << "#{@tab}#{@call_count_type} *#{decl[:function]}_ThrowOnCallCount_HeadTail;\n"
|
||||
@source << "#{@tab}#{@throw_type} *#{decl[:function]}_ThrowValue;\n"
|
||||
@source << "#{@tab}#{@throw_type} *#{decl[:function]}_ThrowValue_Head;\n"
|
||||
@source << "#{@tab}#{@throw_type} *#{decl[:function]}_ThrowValue_HeadTail;\n"
|
||||
|
||||
if (decl[:return] != "void")
|
||||
@source << "#{@tab}#{decl[:return]} *#{decl[:function]}_Return;\n"
|
||||
@source << "#{@tab}#{decl[:return]} *#{decl[:function]}_Return_Head;\n"
|
||||
@source << "#{@tab}#{decl[:return]} *#{decl[:function]}_Return_HeadTail;\n"
|
||||
end
|
||||
|
||||
if (decl[:args_no_var_args] != "void")
|
||||
args = parse_args(decl[:args_no_var_args])
|
||||
args.each do |arg|
|
||||
type = arg[:type].sub(/const/, '').strip
|
||||
@source << "#{@tab}#{type} *#{decl[:function]}_Expected_#{arg[:name]};\n"
|
||||
@source << "#{@tab}#{type} *#{decl[:function]}_Expected_#{arg[:name]}_Head;\n"
|
||||
@source << "#{@tab}#{type} *#{decl[:function]}_Expected_#{arg[:name]}_HeadTail;\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
@source << "} Mock;\n\n"
|
||||
end
|
||||
|
||||
def create_extern_declarations
|
||||
@parser.externs.each do |extern|
|
||||
@source << extern.gsub(/extern/,'') << ";\n"
|
||||
end
|
||||
@source << "extern jmp_buf AbortFrame;\n"
|
||||
@source << "\n"
|
||||
end
|
||||
|
||||
def create_mock_verify_function
|
||||
@source << "void #{@mock_name}_Verify(void)\n{\n"
|
||||
@source << "#{@tab}TEST_ASSERT_EQUAL(0, Mock.allocFailure);\n"
|
||||
@parser.nondefine_functions.each do |function|
|
||||
decl = parse_declaration(function)
|
||||
@source << "#{@tab}TEST_ASSERT_EQUAL_MESSAGE(Mock.#{decl[:function]}_CallsExpected, Mock.#{decl[:function]}_CallCount, \"Function '#{decl[:function]}' called unexpected number of times.\");\n"
|
||||
end
|
||||
@source << "}\n\n"
|
||||
end
|
||||
|
||||
def create_mock_init_function
|
||||
@source << "void #{@mock_name}_Init(void)\n{\n"
|
||||
@source << "#{@tab}#{@mock_name}_Destroy();\n"
|
||||
@source << "}\n\n"
|
||||
end
|
||||
|
||||
def create_mock_destroy_function
|
||||
@source << "void #{@mock_name}_Destroy(void)\n{\n"
|
||||
@parser.nondefine_functions.each do |function|
|
||||
decl = parse_declaration(function)
|
||||
|
||||
if decl[:return] != "void"
|
||||
@source << "#{@tab}if(Mock.#{decl[:function]}_Return_Head)\n"
|
||||
@source << "#{@tab}{\n"
|
||||
@source << "#{@tab}#{@tab}free(Mock.#{decl[:function]}_Return_Head);\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_Return_Head=NULL;\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_Return_HeadTail=NULL;\n"
|
||||
@source << "#{@tab}}\n"
|
||||
end
|
||||
if decl[:args_no_var_args] != "void"
|
||||
args = parse_args(decl[:args_no_var_args])
|
||||
args.each do |arg|
|
||||
@source << "#{@tab}if(Mock.#{decl[:function]}_Expected_#{arg[:name]}_Head)\n"
|
||||
@source << "#{@tab}{\n"
|
||||
@source << "#{@tab}#{@tab}free(Mock.#{decl[:function]}_Expected_#{arg[:name]}_Head);\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_Expected_#{arg[:name]}_Head=NULL;\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_Expected_#{arg[:name]}_HeadTail=NULL;\n"
|
||||
@source << "#{@tab}}\n"
|
||||
end
|
||||
end
|
||||
|
||||
@source << "#{@tab}if(Mock.#{decl[:function]}_ThrowOnCallCount_Head)\n"
|
||||
@source << "#{@tab}{\n"
|
||||
@source << "#{@tab}#{@tab}free(Mock.#{decl[:function]}_ThrowOnCallCount_Head);\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_ThrowOnCallCount_Head=NULL;\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_ThrowOnCallCount_HeadTail=NULL;\n"
|
||||
@source << "#{@tab}}\n"
|
||||
|
||||
@source << "#{@tab}if(Mock.#{decl[:function]}_ThrowValue_Head)\n"
|
||||
@source << "#{@tab}{\n"
|
||||
@source << "#{@tab}#{@tab}free(Mock.#{decl[:function]}_ThrowValue_Head);\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_ThrowValue_Head=NULL;\n"
|
||||
@source << "#{@tab}#{@tab}Mock.#{decl[:function]}_ThrowValue_HeadTail=NULL;\n"
|
||||
@source << "#{@tab}}\n"
|
||||
end
|
||||
@source << "#{@tab}memset(&Mock, 0, sizeof(Mock));\n"
|
||||
@source << "}\n\n"
|
||||
end
|
||||
|
||||
def create_mock_argument_verifier(arguments)
|
||||
@source << "void AssertParameters_#{decl[:function]}(#{decl[:args_no_var_args]})\n{\n"
|
||||
arguments.each do |arg|
|
||||
type = arg[:type].sub(/const/, '').strip
|
||||
@source << make_handle_expected(decl[:function], type, arg[:name])
|
||||
end
|
||||
@source << "}\n\n"
|
||||
end
|
||||
|
||||
def create_call_list(decl)
|
||||
args = parse_args(decl[:args_no_var_args])
|
||||
call_list = ""
|
||||
args.each do |arg|
|
||||
if call_list.empty?
|
||||
call_list = arg[:name]
|
||||
else
|
||||
call_list += ", " + arg[:name]
|
||||
end
|
||||
end
|
||||
return call_list
|
||||
end
|
||||
|
||||
def create_mock_implementation(function)
|
||||
|
||||
decl = parse_declaration(function)
|
||||
|
||||
newtab = "#{@tab}"
|
||||
|
||||
# Create mock argument verifier, if necessary
|
||||
if decl[:args] != "void"
|
||||
args = parse_args(decl[:args_no_var_args])
|
||||
create_mock_argument_verifier(args)
|
||||
end
|
||||
|
||||
# Create mock function
|
||||
@source << "#{decl[:modifier]} " if (!decl[:modifier].nil? && decl[:modifier].length > 0)
|
||||
@source << "#{decl[:return]} #{decl[:function]}(#{decl[:args]})\n"
|
||||
@source << "{\n"
|
||||
|
||||
# start ignore block
|
||||
if (@allow_ignore_mock)
|
||||
newtab = "#{@tab}#{@tab}"
|
||||
@source << "#{@tab}if (!Mock.#{decl[:function]}_IgnoreBool)\n"
|
||||
@source << "#{@tab}{\n"
|
||||
end
|
||||
|
||||
@source << "#{newtab}Mock.#{decl[:function]}_CallCount++;\n"
|
||||
|
||||
#create overcall protection
|
||||
exp = "Mock.#{decl[:function]}_CallsExpected"
|
||||
@source << "#{newtab}if (Mock.#{decl[:function]}_CallCount > Mock.#{decl[:function]}_CallsExpected)\n"
|
||||
@source << "#{newtab}{\n"
|
||||
@source << "#{newtab}#{@tab}TEST_THROW(\"#{decl[:function]} Called More Times Than Expected\");\n"
|
||||
@source << "#{newtab}}\n"
|
||||
|
||||
# Create call to argument verifier, if necessary
|
||||
if decl[:args_no_var_args] != "void"
|
||||
@source << "#{newtab}AssertParameters_#{decl[:function]}(#{create_call_list(decl)});\n"
|
||||
end
|
||||
|
||||
# Throw exception, if appropriate
|
||||
@source << make_handle_throw(decl[:function], @throw_type) if (@use_cexception)
|
||||
|
||||
# end ignore block
|
||||
if (@allow_ignore_mock)
|
||||
@source << "#{@tab}}\n"
|
||||
end
|
||||
|
||||
# Return expected value, if necessary
|
||||
if decl[:return] != "void"
|
||||
@source << make_handle_return(decl[:function], decl[:return])
|
||||
end
|
||||
|
||||
# Close out the function
|
||||
@source << "}\n\n"
|
||||
end
|
||||
|
||||
def create_mock_function_expectation(function)
|
||||
decl = parse_declaration(function)
|
||||
if decl[:args_no_var_args] == "void"
|
||||
# Function has void return type with no arguments
|
||||
if decl[:return] == "void"
|
||||
@source << "void #{decl[:function]}_Expect(void)\n"
|
||||
@source << "{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_CallsExpected++;\n"
|
||||
@source << "}\n\n"
|
||||
# Function has non-void return type with no arguments
|
||||
else
|
||||
@source << "void #{decl[:function]}_ExpectAndReturn(#{decl[:return]} toReturn)\n"
|
||||
@source << "{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_CallsExpected++;\n"
|
||||
@source << make_expand_array(decl[:return], "Mock.#{decl[:function]}_Return_Head", "toReturn")
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_Return = Mock.#{decl[:function]}_Return_Head;\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_Return += Mock.#{decl[:function]}_CallCount;\n"
|
||||
@source << "}\n\n"
|
||||
end
|
||||
else
|
||||
|
||||
# Parse function arguments
|
||||
args = parse_args(decl[:args_no_var_args])
|
||||
|
||||
# Create parameter expectation function
|
||||
@source << "void ExpectParameters_#{decl[:function]}(#{decl[:args_no_var_args]})\n"
|
||||
@source << "{\n"
|
||||
args.each do |arg|
|
||||
type = arg[:type].sub(/const/, '').strip
|
||||
@source << make_add_new_expected(decl[:function], type, arg[:name])
|
||||
end
|
||||
@source << "}\n\n"
|
||||
|
||||
# Function has void return type with arguments
|
||||
if decl[:return] == "void"
|
||||
@source << "void #{decl[:function]}_Expect(#{decl[:args_no_var_args]})\n"
|
||||
@source << "{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_CallsExpected++;\n"
|
||||
@source << "#{@tab}ExpectParameters_#{decl[:function]}(#{create_call_list(decl)});\n"
|
||||
@source << "}\n\n"
|
||||
# Function has non-void return type with arguments
|
||||
else
|
||||
@source << "void #{decl[:function]}_ExpectAndReturn(#{decl[:args_no_var_args]}, #{decl[:return]} toReturn)\n{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_CallsExpected++;\n"
|
||||
@source << "#{@tab}ExpectParameters_#{decl[:function]}(#{create_call_list(decl)});\n"
|
||||
@source << make_expand_array(decl[:return], "Mock.#{decl[:function]}_Return_Head", "toReturn")
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_Return = Mock.#{decl[:function]}_Return_Head;\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_Return += Mock.#{decl[:function]}_CallCount;\n"
|
||||
@source << "}\n\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_mock_function_expectation_with_throw(function)
|
||||
decl = parse_declaration(function)
|
||||
|
||||
# function takes no arguments
|
||||
if decl[:args_no_var_args] == "void"
|
||||
@source << "void #{decl[:function]}_ExpectAndThrow(#{@throw_type} toThrow)\n"
|
||||
@source << "{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_CallsExpected++;\n"
|
||||
@source << make_expand_array(@call_count_type, "Mock.#{decl[:function]}_ThrowOnCallCount_Head", "Mock.#{decl[:function]}_CallsExpected")
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowOnCallCount = Mock.#{decl[:function]}_ThrowOnCallCount_Head;\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowOnCallCount += Mock.#{decl[:function]}_CallCount;\n"
|
||||
@source << make_expand_array(@throw_type, "Mock.#{decl[:function]}_ThrowValue_Head", "toThrow")
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowValue = Mock.#{decl[:function]}_ThrowValue_Head;\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowValue += Mock.#{decl[:function]}_CallCount;\n"
|
||||
@source << "}\n\n"
|
||||
else
|
||||
# Parse function arguments
|
||||
args = parse_args(decl[:args_no_var_args])
|
||||
|
||||
@source << "void #{decl[:function]}_ExpectAndThrow(#{decl[:args_no_var_args]}, #{@throw_type} toThrow)\n"
|
||||
@source << "{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_CallsExpected++;\n"
|
||||
@source << make_expand_array(@call_count_type, "Mock.#{decl[:function]}_ThrowOnCallCount_Head", "Mock.#{decl[:function]}_CallsExpected")
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowOnCallCount = Mock.#{decl[:function]}_ThrowOnCallCount_Head;\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowOnCallCount += Mock.#{decl[:function]}_CallCount;\n"
|
||||
@source << make_expand_array(@throw_type, "Mock.#{decl[:function]}_ThrowValue_Head", "toThrow")
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowValue = Mock.#{decl[:function]}_ThrowValue_Head;\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_ThrowValue += Mock.#{decl[:function]}_CallCount;\n"
|
||||
@source << "#{@tab}ExpectParameters_#{decl[:function]}(#{create_call_list(decl)});\n"
|
||||
@source << "}\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
def create_mock_function_ignore(function)
|
||||
decl = parse_declaration(function)
|
||||
# Function has void return type with no arguments
|
||||
if decl[:return] == "void"
|
||||
@source << "void #{decl[:function]}_Ignore(void)\n"
|
||||
@source << "{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_IgnoreBool = (unsigned char)1;\n"
|
||||
@source << "}\n\n"
|
||||
# Function has non-void return type with no arguments
|
||||
else
|
||||
@source << "void #{decl[:function]}_IgnoreAndReturn(#{decl[:return]} toReturn)\n"
|
||||
@source << "{\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_IgnoreBool = (unsigned char)1;\n"
|
||||
@source << make_expand_array(decl[:return], "Mock.#{decl[:function]}_Return_Head", "toReturn")
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_Return = Mock.#{decl[:function]}_Return_Head;\n"
|
||||
@source << "#{@tab}Mock.#{decl[:function]}_Return += Mock.#{decl[:function]}_CallCount;\n"
|
||||
@source << "}\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
def make_expand_array(type, array, newValue)
|
||||
<<EOS
|
||||
|
||||
#{@tab}{
|
||||
#{@tab}#{@tab}int sz = 0;
|
||||
#{@tab}#{@tab}#{type} *pointer = #{array};
|
||||
#{@tab}#{@tab}while(pointer && pointer != #{array}Tail) { sz++; pointer++; }
|
||||
#{@tab}#{@tab}if(sz == 0)
|
||||
#{@tab}#{@tab}{
|
||||
#{@tab}#{@tab}#{@tab}#{array} = (#{type}*)malloc(2*sizeof(#{type}));
|
||||
#{@tab}#{@tab}#{@tab}if(!#{array})
|
||||
#{@tab}#{@tab}#{@tab}#{@tab}Mock.allocFailure++;
|
||||
#{@tab}#{@tab}}
|
||||
#{@tab}#{@tab}else
|
||||
#{@tab}#{@tab}{
|
||||
#{@tab}#{@tab}#{@tab}#{type} *ptmp = (#{type}*)realloc(#{array}, sizeof(#{type}) * (sz+1));
|
||||
#{@tab}#{@tab}#{@tab}if(!ptmp)
|
||||
#{@tab}#{@tab}#{@tab}#{@tab}Mock.allocFailure++;
|
||||
#{@tab}#{@tab}#{@tab}else
|
||||
#{@tab}#{@tab}#{@tab}#{@tab}#{array} = ptmp;
|
||||
#{@tab}#{@tab}}
|
||||
#{@tab}#{@tab}memcpy(&#{array}[sz], &#{newValue}, sizeof(#{type}));
|
||||
#{@tab}#{@tab}#{array}Tail = &#{array}[sz+1];
|
||||
#{@tab}}
|
||||
EOS
|
||||
end
|
||||
|
||||
def make_handle_throw(method, throw_type)
|
||||
|
||||
newtab = "#{@tab}"
|
||||
|
||||
if (@allow_ignore_mock)
|
||||
newtab = "#{@tab}#{@tab}"
|
||||
end
|
||||
|
||||
<<EOS
|
||||
|
||||
#{newtab}if((Mock.#{method}_ThrowOnCallCount != Mock.#{method}_ThrowOnCallCount_HeadTail) &&
|
||||
#{newtab}#{@tab}(Mock.#{method}_ThrowValue != Mock.#{method}_ThrowValue_HeadTail))
|
||||
#{newtab}{
|
||||
#{newtab}#{@tab}if (*Mock.#{decl[:function]}_ThrowOnCallCount &&
|
||||
#{newtab}#{@tab}#{@tab}(Mock.#{decl[:function]}_CallCount == *Mock.#{decl[:function]}_ThrowOnCallCount))
|
||||
#{newtab}#{@tab}{
|
||||
#{newtab}#{@tab}#{@tab}#{throw_type} toThrow = *Mock.#{decl[:function]}_ThrowValue;
|
||||
#{newtab}#{@tab}#{@tab}Mock.#{decl[:function]}_ThrowOnCallCount++;
|
||||
#{newtab}#{@tab}#{@tab}Mock.#{decl[:function]}_ThrowValue++;
|
||||
#{newtab}#{@tab}#{@tab}Throw(toThrow);
|
||||
#{newtab}#{@tab}}
|
||||
#{newtab}}
|
||||
EOS
|
||||
end
|
||||
|
||||
def make_handle_return(method, return_type)
|
||||
|
||||
return_block = <<EOS
|
||||
|
||||
#{@tab}if(Mock.#{method}_Return != Mock.#{method}_Return_HeadTail)
|
||||
#{@tab}{
|
||||
#{@tab}#{@tab}#{return_type} toReturn = *Mock.#{method}_Return;
|
||||
#{@tab}#{@tab}Mock.#{method}_Return++;
|
||||
#{@tab}#{@tab}return toReturn;
|
||||
#{@tab}}
|
||||
#{@tab}else
|
||||
#{@tab}{
|
||||
#{@tab}#{@tab}return *Mock.#{method}_Return_Head;
|
||||
#{@tab}}
|
||||
EOS
|
||||
return return_block
|
||||
end
|
||||
|
||||
def make_add_new_expected(method, type, expected)
|
||||
array = make_expand_array(type, "Mock.#{method}_Expected_#{expected}_Head", expected)
|
||||
array = "#{array}" + "#{@tab}Mock.#{method}_Expected_#{expected} = Mock.#{method}_Expected_#{expected}_Head;\n"
|
||||
return "#{array}" + "#{@tab}Mock.#{method}_Expected_#{expected} += Mock.#{decl[:function]}_CallCount;\n"
|
||||
end
|
||||
|
||||
def make_handle_expected(method, type, actual)
|
||||
|
||||
if (type == "char*" || type == "const char*")
|
||||
code_block = <<EOS
|
||||
|
||||
#{@tab}if(Mock.#{method}_Expected_#{actual} != Mock.#{method}_Expected_#{actual}_HeadTail)
|
||||
#{@tab}{
|
||||
#{@tab}#{@tab}#{type}* p_expected = Mock.#{method}_Expected_#{actual};
|
||||
#{@tab}#{@tab}Mock.#{method}_Expected_#{actual}++;
|
||||
#{@tab}#{@tab}TEST_ASSERT_EQUAL_STRING_MESSAGE(*p_expected, #{actual}, \"Function '#{method}' called with unexpected string for parameter '#{actual}'.\");
|
||||
#{@tab}}
|
||||
EOS
|
||||
else
|
||||
code_block = <<EOS
|
||||
|
||||
#{@tab}if(Mock.#{method}_Expected_#{actual} != Mock.#{method}_Expected_#{actual}_HeadTail)
|
||||
#{@tab}{
|
||||
#{@tab}#{@tab}#{type}* p_expected = Mock.#{method}_Expected_#{actual};
|
||||
#{@tab}#{@tab}Mock.#{method}_Expected_#{actual}++;
|
||||
#{@tab}#{@tab}TEST_ASSERT_EQUAL_MESSAGE(*p_expected, #{actual}, \"Function '#{method}' called with unexpected value for parameter '#{actual}'.\");
|
||||
#{@tab}}
|
||||
EOS
|
||||
end
|
||||
|
||||
return code_block
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,144 @@
|
||||
class CFileParser
|
||||
attr_accessor :match_type, :attribute_match, :decl_modifier, :decl_return, :decl_function, :decl_args
|
||||
|
||||
def initialize(source, match_type=/\w+\**/, attributes=['static', '__monitor', '__ramfunc', '__irq', '__fiq'])
|
||||
source = source.gsub(/\/\/.*$/, '') #remove line comments
|
||||
source = source.gsub(/\/\*.*?\*\//m, '') #remove block comments
|
||||
@lines = source.split(/(^\s*\#.*$) # Treat preprocessor directives as a logical line
|
||||
| (;|\{|\}) /x) # Match ;, {, and } as end of lines
|
||||
@lines.delete_if {|line| line =~ /\\\n/} #ignore lines that contain continuation lines
|
||||
@lines.delete_if {|line| line =~ /typedef/} #ignore lines that contain typedef statements
|
||||
|
||||
@functions = nil
|
||||
@match_type = match_type
|
||||
@c_attributes = attributes
|
||||
@declaration_parse_matcher = /(\w*\s+)*([^\s]+)\s+(\w+)\s*\(([^\)]*)\)/
|
||||
@attribute_match = Regexp.compile(%|(#{@c_attributes.join('|')}\s+)*|)
|
||||
@included = nil
|
||||
@var_args_ellipsis = '...'
|
||||
end
|
||||
|
||||
def c_attributes=(value)
|
||||
@c_attributes = value
|
||||
@attribute_match = Regexp.compile(%|(#{@c_attributes.join('|')}\s+)*|)
|
||||
end
|
||||
|
||||
def included_files
|
||||
if @included.nil?
|
||||
@included = []
|
||||
@lines.each do |line|
|
||||
if line =~ /#include\s+"(.*)"/
|
||||
@included << $1
|
||||
end
|
||||
end
|
||||
end
|
||||
@included
|
||||
end
|
||||
|
||||
def externs
|
||||
if !@externs
|
||||
@externs = []
|
||||
depth = 0
|
||||
@lines.each do |line|
|
||||
if depth.zero? && line =~ /^\s*extern.*/m
|
||||
@externs << $&.strip.gsub(/\s+/, ' ')
|
||||
end
|
||||
if line =~ /\{/
|
||||
depth += 1
|
||||
end
|
||||
if line =~ /\}/
|
||||
depth -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@externs
|
||||
end
|
||||
|
||||
def functions
|
||||
if @functions.nil?
|
||||
@functions = []
|
||||
depth = 0
|
||||
@lines.each do |line|
|
||||
if depth.zero? && line =~ /#{@attribute_match}\s*#{@match_type}\s+\w+\s*\(.*\)/m
|
||||
@functions << line.strip.gsub(/\s+/, ' ')
|
||||
end
|
||||
if line =~ /\{/
|
||||
depth += 1
|
||||
end
|
||||
if line =~ /\}/
|
||||
depth -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@functions
|
||||
end
|
||||
|
||||
def nonstatic_functions
|
||||
@nonstatic_functions ||= functions.reject do |func|
|
||||
func =~ /\bstatic\b/
|
||||
end
|
||||
end
|
||||
|
||||
def nondefine_functions
|
||||
@nondefine_functions ||= functions.reject do |func|
|
||||
func =~ /\bdefine\b/
|
||||
end
|
||||
@nondefine_functions
|
||||
end
|
||||
|
||||
def parse_args(arg_list)
|
||||
args = []
|
||||
arg_list.split(',').each do |arg|
|
||||
arg = arg.strip
|
||||
return args if ((arg == @var_args_ellipsis) || (arg == 'void'))
|
||||
arg_match = arg.match /^(.+)\s+(\w+)$/
|
||||
raise "Failed parsing argument list at argument: '#{arg}'" if arg_match.nil?
|
||||
type = ''
|
||||
name = arg_match[-1]
|
||||
type = arg_match[1]
|
||||
args << {:name => name, :type => type}
|
||||
end
|
||||
return args
|
||||
end
|
||||
|
||||
def parse_declaration(declaration)
|
||||
decl = {}
|
||||
|
||||
@declaration_parse_matcher.match(declaration)
|
||||
|
||||
modifier = $1
|
||||
modifier = '' if modifier.nil?
|
||||
decl[:modifier] = modifier.strip
|
||||
|
||||
decl[:return] = $2
|
||||
|
||||
decl[:function] = $3
|
||||
|
||||
args = $4
|
||||
#remove default parameter statements from mock definitions
|
||||
args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*\,/, ',')
|
||||
decl_args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ')
|
||||
decl_args.strip!
|
||||
decl[:args] = decl_args
|
||||
|
||||
# ignore variable arguments at end of parameter list
|
||||
if (decl_args == @var_args_ellipsis)
|
||||
decl[:args_no_var_args] = 'void'
|
||||
else
|
||||
decl[:args_no_var_args] = decl_args.sub(/,\s*\.\.\./, '')
|
||||
end
|
||||
|
||||
if decl[:return].nil? or decl[;function].nil? or decl[:args].nil?
|
||||
raise "Declaration parse failed!\n" +
|
||||
" declaration: #{declaration}\n" +
|
||||
" modifier: #{decl[:modifier]}\n" +
|
||||
" return: #{decl[:return]}\n" +
|
||||
" function: #{decl[:function]}\n" +
|
||||
" args:#{decl[:args]}\n" +
|
||||
" args_no_var_args:#{decl[:args_no_var_args]}"
|
||||
end
|
||||
|
||||
return decl
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
class CMock
|
||||
attr_accessor :mocks_path, :includes, :interface_parser
|
||||
|
||||
def initialize(mocks_path='mocks', includes=[], interface_parser=nil)
|
||||
@mocks_path = mocks_path
|
||||
@includes = includes
|
||||
@interface_parser = interface_parser
|
||||
end
|
||||
|
||||
def generate(module_header)
|
||||
@interface_parser.extract_interface(module_header)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
$here = File.dirname __FILE__
|
||||
require "#{$here}/cmock_generator"
|
||||
|
||||
class CMockSetup
|
||||
|
||||
attr_accessor :mocks_path, :auto_path
|
||||
|
||||
def initialize(mocks_path='mocks', includes=[], use_cexception=true, allow_ignore_mock=false)
|
||||
@mocks_path = mocks_path
|
||||
@includes = includes
|
||||
@use_cexception = use_cexception
|
||||
@allow_ignore_mock = allow_ignore_mock
|
||||
end
|
||||
|
||||
def setup_mocks(files)
|
||||
files.each do |src|
|
||||
generate_mock src
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_mock(src)
|
||||
name = File.basename(src, '.h')
|
||||
path = File.dirname(src)
|
||||
cmg = CMockGenerator.new(name, path, @mocks_path, @includes, @use_cexception, @allow_ignore_mock)
|
||||
$stderr.puts "Creating mock for #{name}..."
|
||||
$stderr.flush
|
||||
cmg.create_mock
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user