# ========================================== # 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, :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 @ordered = @config.enforce_strict_ordering @framework = @config.framework.to_s @fail_on_unexpected_calls = @config.fail_on_unexpected_calls @subdir = @config.subdir @includes_h_pre_orig_header = (@config.includes || @config.includes_h_pre_orig_header || []).map{|h| h =~ / 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, typedefs) file << "\n" typedefs.each {|typedef| file << "#{typedef}\n" } file << "\n\n" end def create_mock_header_service_call_declarations(file) file << "void #{@clean_mock_name}_Init(void);\n" file << "void #{@clean_mock_name}_Destroy(void);\n" file << "void #{@clean_mock_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, functions) header_file = (@subdir ? @subdir + '/' : '') + filename.gsub(".c",".h") file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" file << "#include \n" file << "#include \n" file << "#include \n" 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 = [] 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, 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 #{@clean_mock_name}Instance\n{\n" if (functions.size == 0) 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) file << "extern jmp_buf AbortFrame;\n" if (@ordered) file << "extern int GlobalExpectCount;\n" file << "extern int GlobalVerifyOrder;\n" end file << "\n" end def create_mock_verify_function(file, functions) file << "void #{@clean_mock_name}_Verify(void)\n{\n" verifications = 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) file << "void #{@clean_mock_name}_Init(void)\n{\n" file << " #{@clean_mock_name}_Destroy();\n" file << "}\n\n" end def create_mock_destroy_function(file, functions) file << "void #{@clean_mock_name}_Destroy(void)\n{\n" file << " CMock_Guts_MemFreeAll();\n" file << " memset(&Mock, 0, sizeof(Mock));\n" file << functions.collect {|function| @plugins.run(:mock_destroy, function)}.join unless (@fail_on_unexpected_calls) file << 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?) # Create mock function if (not @weak.empty?) file << "#if defined (__IAR_SYSTEMS_ICC__)\n" file << "#pragma weak #{function[:name]}\n" file << "#else\n" file << "#{function_mod_and_rettype} #{function[:name]}(#{args_string}) #{weak};\n" file << "#endif\n\n" end file << "#{function_mod_and_rettype} #{function[: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\n" end def create_mock_interfaces(file, function) file << @utils.code_add_argument_loader(function) file << @plugins.run(:mock_interfaces, function) end end