git-svn-id: http://cmock.svn.sourceforge.net/svnroot/cmock/trunk@5 bf332499-1b4d-0410-844d-d2d48d5cc64c

This commit is contained in:
greg-williams
2008-06-01 18:09:28 +00:00
parent ae5e443d6f
commit 42fba80136
5 changed files with 739 additions and 15 deletions
+4 -15
View File
@@ -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"
+545
View File
@@ -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
+144
View File
@@ -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
+15
View File
@@ -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
+31
View File
@@ -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