diff --git a/docs/CMock_Summary.md b/docs/CMock_Summary.md index c352693..eb312b5 100644 --- a/docs/CMock_Summary.md +++ b/docs/CMock_Summary.md @@ -1,82 +1,82 @@ [All code is copyright © 2007-2010 Cmock Project by Mike Karlesky, Mark VanderVoord, and Greg Williams. -This Documentation Is Released Under a Creative Commons 3.0 -Attribution Share-Alike License] +This Documentation Is Released Under a Creative Commons 3.0 +Attribution Share-Alike License] What the What? ============== -CMock is a nice little tool which takes your header files and creates -a Mock interface for it so that you can more easily Unit test modules -that touch other modules. For each function prototype in your -header, like this one: +CMock is a nice little tool which takes your header files and creates +a Mock interface for it so that you can more easily Unit test modules +that touch other modules. For each function prototype in your +header, like this one: int DoesSomething(int a, int b); - -...you get an automatically generated DoesSomething function -that you can link to instead of your real DoesSomething function. -By using this Mocked version, you can then verify that it receives -the data you want, and make it return whatever data you desire, -make it throw errors when you want, and more... Create these for -everything your latest real module touches, and you're suddenly -in a position of power: You can control and verify every detail -of your latest creation. -To make that easier, CMock also gives you a bunch of functions -like the ones below, so you can tell that generated DoesSomething -function how to behave for each test: +...you get an automatically generated DoesSomething function +that you can link to instead of your real DoesSomething function. +By using this Mocked version, you can then verify that it receives +the data you want, and make it return whatever data you desire, +make it throw errors when you want, and more... Create these for +everything your latest real module touches, and you're suddenly +in a position of power: You can control and verify every detail +of your latest creation. + +To make that easier, CMock also gives you a bunch of functions +like the ones below, so you can tell that generated DoesSomething +function how to behave for each test: void DoesSomething_ExpectAndReturn(int a, int b, int toReturn); void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error); void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback); void DoesSomething_IgnoreAndReturn(int toReturn); - -You can pile a bunch of these back to back, and it remembers what -you wanted to pass when, like so: + +You can pile a bunch of these back to back, and it remembers what +you wanted to pass when, like so: test_CallsDoesSomething_ShouldDoJustThat(void) { DoesSomething_ExpectAndReturn(1,2,3); DoesSomething_ExpectAndReturn(4,5,6); DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS); - + CallsDoesSomething( ); } - -This test will call CallsDoesSomething, which is the function -we are testing. We are expecting that function to call DoesSomething -three times. The first time, we check to make sure it's called -as DoesSomething(1, 2) and we'll magically return a 3. The second -time we check for DoesSomething(4, 5) and we'll return a 6. The -third time we verify DoesSomething(7, 8) and we'll throw an error -instead of returning anything. If CallsDoesSomething gets -any of this wrong, it fails the test. It will fail if you didn't -call DoesSomething enough, or too much, or with the wrong arguments, -or in the wrong order. -CMock is based on Unity, which it uses for all internal testing. -It uses Ruby to do all the main work (versions 1.8.6 through 1.9.2). +This test will call CallsDoesSomething, which is the function +we are testing. We are expecting that function to call DoesSomething +three times. The first time, we check to make sure it's called +as DoesSomething(1, 2) and we'll magically return a 3. The second +time we check for DoesSomething(4, 5) and we'll return a 6. The +third time we verify DoesSomething(7, 8) and we'll throw an error +instead of returning anything. If CallsDoesSomething gets +any of this wrong, it fails the test. It will fail if you didn't +call DoesSomething enough, or too much, or with the wrong arguments, +or in the wrong order. + +CMock is based on Unity, which it uses for all internal testing. +It uses Ruby to do all the main work (versions 1.8.6 through 1.9.2). Generated Mock Module Summary ============================= -In addition to the mocks themselves, CMock will generate the -following functions for use in your tests. The expect functions -are always generated. The other functions are only generated -if those plugins are enabled: +In addition to the mocks themselves, CMock will generate the +following functions for use in your tests. The expect functions +are always generated. The other functions are only generated +if those plugins are enabled: Expect: ------- -Your basic staple Expects which will be used for most of your day -to day CMock work. +Your basic staple Expects which will be used for most of your day +to day CMock work. * `void func(void)` => `void func_Expect(void)` * `void func(params)` => `void func_Expect(expected_params)` @@ -87,9 +87,9 @@ to day CMock work. Array: ------ -An ExpectWithArray will check as many elements as you specify. -If you specify zero elements, it will check just the pointer if -`:smart` mode is configured or fail if `:compare_data` is set. +An ExpectWithArray will check as many elements as you specify. +If you specify zero elements, it will check just the pointer if +`:smart` mode is configured or fail if `:compare_data` is set. * `void func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers) * `void func(ptr * param, other)` => `void func_ExpectWithArray(ptr* param, int param_depth, other)` @@ -100,11 +100,11 @@ If you specify zero elements, it will check just the pointer if Callback: --------- -As soon as you stub a callback in a test, it will call the callback -whenever the mock is encountered and return the retval returned -from the callback (if any) instead of performing the usual expect -checks. It can be configured to check the arguments first (like -expects) or just jump directly to the callback. +As soon as you stub a callback in a test, it will call the callback +whenever the mock is encountered and return the retval returned +from the callback (if any) instead of performing the usual expect +checks. It can be configured to check the arguments first (like +expects) or just jump directly to the callback. * `void func(void)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)` where `CMOCK_func_CALLBACK` looks like: `void func(int NumCalls)` @@ -119,10 +119,10 @@ where `CMOCK_func_CALLBACK` looks like: `retval func(params, int NumCalls)` Cexception: ----------- -If you are using Cexception for error handling, you can use this -to throw errors from inside mocks. Like Expects, it remembers -which call was supposed to throw the error, and it still checks -parameters. +If you are using Cexception for error handling, you can use this +to throw errors from inside mocks. Like Expects, it remembers +which call was supposed to throw the error, and it still checks +parameters. * `void func(void)` => `void func_ExpectAndThrow(value_to_throw)` * `void func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)` @@ -133,10 +133,10 @@ parameters. Ignore: ------- -This plugin supports two modes. You can use it to force CMock to -ignore calls to specific functions or to just ignore the arguments -passed to those functions. Either way you can specify multiple -returns or a single value to always return, whichever you prefer. +This plugin supports two modes. You can use it to force CMock to +ignore calls to specific functions or to just ignore the arguments +passed to those functions. Either way you can specify multiple +returns or a single value to always return, whichever you prefer. * `void func(void)` => `void func_Ignore(void)` * `void func(params)` => `void func_Ignore(void)` @@ -147,59 +147,59 @@ returns or a single value to always return, whichever you prefer. Running CMock ============= -CMock is a Ruby script and class. You can therefore use it directly -from the command line, or include it in your own scripts or rakefiles. +CMock is a Ruby script and class. You can therefore use it directly +from the command line, or include it in your own scripts or rakefiles. Mocking from the Command Line ----------------------------- -After unpacking CMock, you will find CMock.rb in the 'lib' directory. -This is the file that you want to run. It takes a list of header files -to be mocked, as well as an optional yaml file for a more detailed -configuration (see config options below). +After unpacking CMock, you will find CMock.rb in the 'lib' directory. +This is the file that you want to run. It takes a list of header files +to be mocked, as well as an optional yaml file for a more detailed +configuration (see config options below). -For example, this will create three mocks using the configuration -specified in MyConfig.yml: +For example, this will create three mocks using the configuration +specified in MyConfig.yml: ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h -And this will create two mocks using the default configuration: +And this will create two mocks using the default configuration: ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h Mocking From Scripts or Rake ---------------------------- -CMock can be used directly from your own scripts or from a rakefile. -Start by including cmock.rb, then create an instance of CMock. -When you create your instance, you may initialize it in one of -three ways. +CMock can be used directly from your own scripts or from a rakefile. +Start by including cmock.rb, then create an instance of CMock. +When you create your instance, you may initialize it in one of +three ways. -You may specify nothing, allowing it to run with default settings: +You may specify nothing, allowing it to run with default settings: cmock = CMock.new -You may specify a YAML file containing the configuration options -you desire: +You may specify a YAML file containing the configuration options +you desire: cmock = CMock.new('../MyConfig.yml') -You may specify the options explicitly: +You may specify the options explicitly: cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => 'my/mocks/') -Config Options: +Config Options: -The following configuration options can be specified in the -yaml file or directly when instantiating. +The following configuration options can be specified in the +yaml file or directly when instantiating. -Passed as Ruby, they look like this: +Passed as Ruby, they look like this: { :attributes => [“__funky”, “__intrinsic”], :when_ptr => :compare } -Defined in the yaml file, they look more like this: +Defined in the yaml file, they look more like this: - :cmock: + :cmock: :attributes: - __funky - __intrinsic @@ -238,11 +238,6 @@ Defined in the yaml file, they look more like this: unity test frameworks (or if you write one for us), they'll get added here. -* `:ignore`: - Tell `:ignore` plugin to ignore `:args_only` or `:args_and_calls` - (default) where it doesn't even care how many times the mock was - called - * `:includes`: An array of additional include files which should be added to the mocks. Useful for global types and definitions used in your project. @@ -320,37 +315,37 @@ Defined in the yaml file, they look more like this: Compiled Options: ----------------- -A number of #defines also exist for customizing the cmock experience. +A number of #defines also exist for customizing the cmock experience. -CMOCK_MEM_STATIC or CMOCK_MEM_DYNAMIC +CMOCK_MEM_STATIC or CMOCK_MEM_DYNAMIC -Define one of these to determine if you want to dynamically add -memory during tests as required from the heap. If static, you -can control the total footprint of Cmock. If dynamic, you will -need to make sure you make some heap space available for Cmock. +Define one of these to determine if you want to dynamically add +memory during tests as required from the heap. If static, you +can control the total footprint of Cmock. If dynamic, you will +need to make sure you make some heap space available for Cmock. -CMOCK_MEM_SIZE +CMOCK_MEM_SIZE -In static mode this is the total amount of memory you are allocating -to Cmock. In Dynamic mode this is the size of each chunk allocated -at once (larger numbers grab more memory but require less mallocs). +In static mode this is the total amount of memory you are allocating +to Cmock. In Dynamic mode this is the size of each chunk allocated +at once (larger numbers grab more memory but require less mallocs). -CMOCK_MEM_ALIGN +CMOCK_MEM_ALIGN -The way to align your data to. Not everything is as flexible as -a PC, as most embedded designers know. This defaults to 2, meaning -align to the closest 2^2 -> 4 bytes (32 bits). You can turn off alignment -by setting 0, force alignment to the closest uint16 with 1 or even -to the closest uint64 with 3. +The way to align your data to. Not everything is as flexible as +a PC, as most embedded designers know. This defaults to 2, meaning +align to the closest 2^2 -> 4 bytes (32 bits). You can turn off alignment +by setting 0, force alignment to the closest uint16 with 1 or even +to the closest uint64 with 3. -CMOCK_MEM_PTR_AS_INT +CMOCK_MEM_PTR_AS_INT -This is used internally to hold pointers... it needs to be big -enough. On most processors a pointer is the same as an unsigned -long... but maybe that's not true for yours? +This is used internally to hold pointers... it needs to be big +enough. On most processors a pointer is the same as an unsigned +long... but maybe that's not true for yours? -CMOCK_MEM_INDEX_TYPE +CMOCK_MEM_INDEX_TYPE -This needs to be something big enough to point anywhere in Cmock's -memory space... usually it's an unsigned int. +This needs to be something big enough to point anywhere in Cmock's +memory space... usually it's an unsigned int. diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index 34b27a6..3ad15a0 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -2,11 +2,11 @@ # 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 - - CMockDefaultOptions = + + CMockDefaultOptions = { :framework => :unity, :mock_path => 'mocks', @@ -24,24 +24,23 @@ class CMockConfig :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 - :ignore => :args_and_calls, #the options being :args_and_calls or :args_only :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 => nil, + :includes_h_pre_orig_header => nil, + :includes_h_post_orig_header => nil, + :includes_c_pre_header => nil, :includes_c_post_header => nil } - + def initialize(options=nil) case(options) - when NilClass then options = CMockDefaultOptions.clone + when NilClass then options = CMockDefaultOptions.clone when String then options = CMockDefaultOptions.clone.merge(load_config_file_from_yaml(options)) when Hash then options = CMockDefaultOptions.clone.merge(options) else raise "If you specify arguments, it should be a filename or a hash of options" end - + #do some quick type verification [:plugins, :attributes, :treat_as_void].each do |opt| unless (options[opt].class == Array) @@ -59,30 +58,30 @@ class CMockConfig options[:plugins].compact! options[:plugins].map! {|p| p.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 { |key| eval("def #{key.to_s}() return @options[:#{key.to_s}] end") } end - + def load_config_file_from_yaml yaml_filename require 'yaml' require 'fileutils' YAML.load_file(yaml_filename)[:cmock] end - + def set_path(path) @src_path = path end - + def load_unity_helper return File.new(@options[:unity_helper_path]).read if (@options[:unity_helper_path]) return nil end - def standard_treat_as_map + def standard_treat_as_map { 'int' => 'INT', 'char' => 'INT8', diff --git a/lib/cmock_generator_plugin_expect_any_args.rb b/lib/cmock_generator_plugin_expect_any_args.rb new file mode 100644 index 0000000..8faa89f --- /dev/null +++ b/lib/cmock_generator_plugin_expect_any_args.rb @@ -0,0 +1,67 @@ +# ========================================== +# 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_structure(function) + if (function[:return][:void?]) || (@config.plugins.include? :ignore) + "" + else + " #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" + end + end + + def instance_typedefs(function) + " CMOCK_ARG_MODE IgnoreMode;\n" + end + + def mock_function_declarations(function) + + if (function[:return][:void?]) + return "#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" + + "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n" + else + return "#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_implementation(function) + lines = " if (cmock_call_instance->IgnoreMode == CMOCK_ARG_NONE)\n {\n" + if (function[:return][:void?]) + lines << " return;\n }\n" + else + retval = function[:return].merge( { :name => "cmock_call_instance->ReturnVal"} ) + 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 = "" + if (function[:return][:void?]) + lines << "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line)\n{\n" + else + lines << "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->IgnoreMode = CMOCK_ARG_NONE;\n" + lines << "}\n\n" + end +end diff --git a/lib/cmock_generator_plugin_ignore.rb b/lib/cmock_generator_plugin_ignore.rb index 6ab4872..807afd6 100644 --- a/lib/cmock_generator_plugin_ignore.rb +++ b/lib/cmock_generator_plugin_ignore.rb @@ -2,28 +2,19 @@ # 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 - if (@config.ignore == :args_and_calls) - alias :mock_implementation_precheck :mock_implementation_for_ignores - alias :mock_implementation :nothing - alias :mock_verify :mock_conditionally_verify_counts - else - alias :mock_implementation :mock_implementation_for_ignores - alias :mock_implementation_precheck :nothing - alias :mock_verify :nothing - end @utils = utils @priority = 2 end - + def instance_structure(function) if (function[:return][:void?]) " int #{function[:name]}_IgnoreBool;\n" @@ -31,24 +22,19 @@ class CMockGeneratorPluginIgnore " int #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" end end - + def mock_function_declarations(function) if (function[:return][:void?]) - if (@config.ignore == :args_only) - return "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore(__LINE__)\n" + - "void #{function[:name]}_CMockIgnore(UNITY_LINE_TYPE cmock_line);\n" - else - return "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" + - "void #{function[:name]}_CMockIgnore(void);\n" - end - else + return "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" + + "void #{function[:name]}_CMockIgnore(void);\n" + else return "#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 + end end - - def mock_implementation_for_ignores(function) - lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" + + def mock_implementation_precheck(function) + lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" if (function[:return][:void?]) lines << " return;\n }\n" else @@ -59,22 +45,15 @@ class CMockGeneratorPluginIgnore end lines end - + def mock_interfaces(function) lines = "" - args_only = (@config.ignore == :args_only) if (function[:return][:void?]) - if (args_only) - lines << "void #{function[:name]}_CMockIgnore(UNITY_LINE_TYPE cmock_line)\n{\n" - else - lines << "void #{function[:name]}_CMockIgnore(void)\n{\n" - end + lines << "void #{function[:name]}_CMockIgnore(void)\n{\n" else lines << "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" end - if (args_only) - lines << @utils.code_add_base_expectation(function[:name], true) - elsif (!function[:return][:void?]) + if (!function[:return][:void?]) lines << @utils.code_add_base_expectation(function[:name], false) end unless (function[:return][:void?]) @@ -84,12 +63,8 @@ class CMockGeneratorPluginIgnore lines << "}\n\n" end - def mock_conditionally_verify_counts(function) + def mock_conditionally_verify(function) func_name = function[:name] " if (Mock.#{func_name}_IgnoreBool)\n Mock.#{func_name}_CallInstance = CMOCK_GUTS_NONE;\n" end - - def nothing(function) - return "" - end end diff --git a/lib/cmock_generator_utils.rb b/lib/cmock_generator_utils.rb index ce8bf91..4192407 100644 --- a/lib/cmock_generator_utils.rb +++ b/lib/cmock_generator_utils.rb @@ -2,7 +2,7 @@ # 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 @@ -14,22 +14,23 @@ class CMockGeneratorUtils @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 @treat_as = @config.treat_as @helpers = helpers - + if (@arrays) case(@ptr_handling) - when :smart then alias :code_verify_an_arg_expectation :code_verify_an_arg_expectation_with_smart_arrays - when :compare_data then alias :code_verify_an_arg_expectation :code_verify_an_arg_expectation_with_normal_arrays + when :smart then alias :code_verify_an_arg_expectation :code_verify_an_arg_expectation_with_smart_arrays + when :compare_data then alias :code_verify_an_arg_expectation :code_verify_an_arg_expectation_with_normal_arrays when :compare_ptr then raise "ERROR: the array plugin doesn't enjoy working with :compare_ptr only. Disable one option." end else alias :code_verify_an_arg_expectation :code_verify_an_arg_expectation_with_no_arrays 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" @@ -38,37 +39,38 @@ class CMockGeneratorUtils lines << " cmock_call_instance->LineNumber = cmock_line;\n" lines << " cmock_call_instance->CallOrder = ++GlobalExpectCount;\n" if (@ordered and global_ordering_supported) lines << " cmock_call_instance->ExceptionToThrow = CEXCEPTION_NONE;\n" if (@cexception) + lines << " cmock_call_instance->IgnoreMode = CMOCK_ARG_ALL;\n" if (@expect_any) lines end - - def code_add_an_arg_expectation(arg, depth=1) + + 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 and (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 and ptr_or_str?(arg[:type]) and not arg[:const?]) lines end - - def code_assign_argument_quickly(dest, arg) + + def code_assign_argument_quickly(dest, arg) if (arg[:ptr?] or @treat_as.include?(arg[:type])) " #{dest} = #{arg[:const?] ? "(#{arg[:type]})" : ''}#{arg[:name]};\n" else " memcpy(&#{dest}, &#{arg[:name]}, sizeof(#{arg[:type]}));\n" end end - + def code_add_argument_loader(function) if (function[:args_string] != "void") if (@arrays) - args_string = function[:args].map do |m| + args_string = function[:args].map do |m| const_str = m[ :const? ] ? 'const ' : '' m[:ptr?] ? "#{const_str}#{m[:type]} #{m[:name]}, int #{m[:name]}_Depth" : "#{const_str}#{m[:type]} #{m[:name]}" end.join(', ') - "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\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{\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 @@ -76,25 +78,25 @@ class CMockGeneratorUtils "" end end - + def code_call_argument_loader(function) if (function[:args_string] != "void") args = function[:args].map do |m| (@arrays and m[:ptr?]) ? "#{m[:name]}, 1" : m[:name] end - " CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n" + " CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n" else "" end end - + def ptr_or_str?(arg_type) return (arg_type.include? '*' or @treat_as.fetch(arg_type, "").include? '*') end #private ###################### - + def lookup_expect_type(function, arg) c_type = arg[:type] arg_name = arg[:name] @@ -108,7 +110,7 @@ class CMockGeneratorUtils unity_msg = "Function '#{function[:name]}' called with unexpected value for argument '#{arg_name}'." return c_type, arg_name, expected, ignore, unity_func[0], unity_func[1], unity_msg end - + def code_verify_an_arg_expectation_with_no_arrays(function, arg) c_type, arg_name, expected, ignore, unity_func, pre, unity_msg = lookup_expect_type(function, arg) lines = "" @@ -129,12 +131,12 @@ class CMockGeneratorUtils lines << " else\n" lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, \"#{unity_msg}\"); }\n" else - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, \"#{unity_msg}\");\n" + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, \"#{unity_msg}\");\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, unity_msg = lookup_expect_type(function, arg) depth_name = (arg[:ptr?]) ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 @@ -160,12 +162,12 @@ class CMockGeneratorUtils lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, \"#{unity_msg}\"); }\n" end else - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, \"#{unity_msg}\");\n" + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, \"#{unity_msg}\");\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, unity_msg = lookup_expect_type(function, arg) depth_name = (arg[:ptr?]) ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 @@ -193,10 +195,10 @@ class CMockGeneratorUtils lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, \"#{unity_msg}\"); }\n" end else - lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, \"#{unity_msg}\");\n" + lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, \"#{unity_msg}\");\n" end lines << " }\n" lines end - + end diff --git a/src/cmock.h b/src/cmock.h index 4dcfc1f..bd80d4e 100644 --- a/src/cmock.h +++ b/src/cmock.h @@ -14,6 +14,10 @@ #define CMOCK_GUTS_NONE (0) +#define CMOCK_ARG_MODE CMOCK_MEM_INDEX_TYPE +#define CMOCK_ARG_ALL 0 +#define CMOCK_ARG_NONE ((CMOCK_MEM_INDEX_TYPE)(~0)) + //------------------------------------------------------- // Memory API //------------------------------------------------------- diff --git a/targets/gcc_64.yml b/targets/gcc_64.yml new file mode 100644 index 0000000..44f8c17 --- /dev/null +++ b/targets/gcc_64.yml @@ -0,0 +1,54 @@ +--- +compiler: + path: gcc + source_path: &systest_generated_path 'test/system/generated/' + unit_tests_path: &unit_tests_path 'examples/test/' + mocks_path: &systest_mocks_path 'test/system/generated/' + build_path: &systest_build_path 'test/system/build/' + options: + - '-c' + - '-m64' + - '-Wall' + - '-Wunused-parameter' + - '-Wno-address' + - '-std=c99' + - '-pedantic' + includes: + prefix: '-I' + items: + - *systest_generated_path + - *unit_tests_path + - *systest_mocks_path + - 'src/' + - 'vendor/unity/src/' + - 'vendor/c_exception/lib/' + - 'test/system/test_compilation/' + - 'test/' + defines: + prefix: '-D' + items: + - 'UNITY_SUPPORT_64' + object_files: + prefix: '-o' + extension: '.o' + destination: *systest_build_path + +linker: + path: gcc + options: + - -lm + - '-m64' + includes: + prefix: '-I' + object_files: + path: *systest_build_path + extension: '.o' + bin_files: + prefix: '-o' + extension: '.exe' + destination: *systest_build_path + +unsupported: + - callingconv + +colour: true diff --git a/test/system/test_interactions/all_plugins_but_other_limits.yml b/test/system/test_interactions/all_plugins_but_other_limits.yml index de9b588..3053755 100644 --- a/test/system/test_interactions/all_plugins_but_other_limits.yml +++ b/test/system/test_interactions/all_plugins_but_other_limits.yml @@ -3,12 +3,12 @@ :cmock: :enforce_strict_ordering: 1 :treat_externs: :include - :ignore: :args_only :plugins: - :array - :cexception - :ignore - :callback + - :expect_any_args :systest: :types: | @@ -28,9 +28,9 @@ int mixed(int a, int* b, int c); void no_args(void); - :source: - :header: | - #include "CException.h" + :source: + :header: | + #include "CException.h" void function_a(void); void function_b(void); void function_c(void); @@ -38,40 +38,40 @@ void function_e(void); :code: | - void function_a(void) + void function_a(void) { foo(bar()); - } + } void function_b(void) { fooa(bar()); } - + void function_c(void) { CEXCEPTION_T e; Try { foos(bars()); } Catch(e) { foos("err"); } } - + int function_d(void) { int test_list[] = { 1, 2, 3, 4, 5 }; no_pointers(1, "silly"); return mixed(6, test_list, 7); } - + void function_e(void) { foos("Hello"); foos("Tuna"); foos("Oranges"); } - + :tests: :common: | #include "CException.h" void setUp(void) {} void tearDown(void) {} - + :units: - :pass: TRUE :should: 'handle the situation where we pass nulls to pointers' @@ -80,10 +80,10 @@ { bar_ExpectAndReturn(NULL); foo_Expect(NULL); - + function_a(); } - + - :pass: FALSE :should: 'handle the situation where we expected nulls to pointers but did not get that' :code: | @@ -92,10 +92,10 @@ POINT_T pt = {1, 2}; bar_ExpectAndReturn(&pt); foo_Expect(NULL); - + function_a(); } - + - :pass: FALSE :should: 'handle the situation where we did not expect nulls to pointers but got null' :code: | @@ -104,10 +104,10 @@ POINT_T ex = {1, 2}; bar_ExpectAndReturn(NULL); foo_Expect(&ex); - + function_a(); } - + - :pass: FALSE :should: 'handle the situation where we pass single object with expect and it is wrong' :code: | @@ -117,10 +117,10 @@ POINT_T ex = {1, 3}; bar_ExpectAndReturn(&pt); foo_Expect(&ex); - + function_a(); } - + - :pass: TRUE :should: 'handle the situation where we pass single object with expect and use array handler' :code: | @@ -130,10 +130,10 @@ POINT_T ex = {1, 2}; bar_ExpectAndReturn(&pt); foo_ExpectWithArray(&ex, 1); - + function_a(); } - + - :pass: FALSE :should: 'handle the situation where we pass single object with expect and use array handler and it is wrong' :code: | @@ -143,10 +143,10 @@ POINT_T ex = {1, 3}; bar_ExpectAndReturn(&pt); foo_ExpectWithArray(&ex, 1); - + function_a(); } - + - :pass: TRUE :should: 'handle the situation where we pass multiple objects with expect and use array handler' :code: | @@ -156,10 +156,10 @@ POINT_T ex[] = {{1, 2}, {3, 4}, {5, 6}}; bar_ExpectAndReturn(pt); foo_ExpectWithArray(ex, 3); - + function_a(); } - + - :pass: FALSE :should: 'handle the situation where we pass multiple objects with expect and use array handler and it is wrong at end' :code: | @@ -169,7 +169,7 @@ POINT_T ex[] = {{1, 2}, {3, 4}, {5, 9}}; bar_ExpectAndReturn(pt); foo_ExpectWithArray(ex, 3); - + function_a(); } @@ -182,7 +182,7 @@ POINT_T ex = {1, 2}; bar_ExpectAndReturn(&pt); fooa_Expect(&ex); - + function_b(); } @@ -193,10 +193,10 @@ { bars_ExpectAndReturn("This is a\0 silly string"); foos_Expect("This is a\0 wacky string"); - + function_c(); } - + - :pass: FALSE :should: 'handle standard c string as null terminated and not do crappy memory compares of a byte, finding failures' :code: | @@ -204,7 +204,7 @@ { bars_ExpectAndReturn("This is a silly string"); foos_Expect("This is a wacky string"); - + function_c(); } @@ -216,7 +216,7 @@ int expect_list[] = { 1, 9 }; no_pointers_Expect(1, "silly"); mixed_ExpectAndReturn(6, expect_list, 7, 13); - + TEST_ASSERT_EQUAL(13, function_d()); } @@ -228,7 +228,7 @@ int expect_list[] = { 9, 1 }; no_pointers_Expect(1, "silly"); mixed_ExpectAndReturn(6, expect_list, 7, 13); - + TEST_ASSERT_EQUAL(13, function_d()); } @@ -240,7 +240,7 @@ int expect_list[] = { 1, 2, 3, 4, 6 }; no_pointers_Expect(1, "silly"); mixed_ExpectWithArrayAndReturn(6, expect_list, 4, 7, 13); - + TEST_ASSERT_EQUAL(13, function_d()); } @@ -252,7 +252,7 @@ int expect_list[] = { 1, 2, 3, 4, 6 }; no_pointers_Expect(1, "silly"); mixed_ExpectWithArrayAndReturn(6, expect_list, 5, 7, 13); - + TEST_ASSERT_EQUAL(13, function_d()); } @@ -264,7 +264,7 @@ bars_ExpectAndReturn("This is a\0 silly string"); foos_ExpectAndThrow("This is a\0 wacky string", 55); foos_Expect("err"); - + function_c(); } @@ -276,7 +276,7 @@ bars_ExpectAndReturn("This is a\0 silly string"); foos_ExpectAndThrow("This is a\0 wacky string", 55); foos_Expect("wrong error"); - + function_c(); } @@ -288,53 +288,53 @@ int expect_list[] = { 1, 2, 3, 4, 6 }; mixed_ExpectWithArrayAndReturn(6, expect_list, 4, 7, 13); no_pointers_Expect(1, "silly"); - + TEST_ASSERT_EQUAL(13, function_d()); } - :pass: TRUE - :should: 'properly ignore first function but the other will work properly' + :should: 'properly ExpectAnyArgs first function but the other will work properly' :code: | test() { int expect_list[] = { 1, 2, 3, 4, 6 }; - no_pointers_Ignore(); + no_pointers_ExpectAnyArgs(); mixed_ExpectWithArrayAndReturn(6, expect_list, 4, 7, 13); - + TEST_ASSERT_EQUAL(13, function_d()); } - :pass: TRUE - :should: 'properly ignore last function but the other will work properly' + :should: 'properly ExpectAnyArgs last function but the other will work properly' :code: | test() { no_pointers_Expect(1, "silly"); - mixed_IgnoreAndReturn(13); - + mixed_ExpectAnyArgsAndReturn(13); + TEST_ASSERT_EQUAL(13, function_d()); } - :pass: TRUE - :should: 'be ok if we ignore a call each because we are counting calls' + :should: 'be ok if we ExpectAnyArgs a call each because we are counting calls' :code: | test() { - foos_Ignore(); - foos_Ignore(); - foos_Ignore(); - + foos_ExpectAnyArgs(); + foos_ExpectAnyArgs(); + foos_ExpectAnyArgs(); + function_e(); } - :pass: FALSE - :should: 'fail if we do not ignore a call once because we are counting calls' + :should: 'fail if we do not ExpectAnyArgs a call once because we are counting calls' :code: | test() { - foos_Ignore(); - foos_Ignore(); - + foos_ExpectAnyArgs(); + foos_ExpectAnyArgs(); + function_e(); } diff --git a/test/system/test_interactions/ignore_args_only.yml b/test/system/test_interactions/expect_any_args.yml similarity index 61% rename from test/system/test_interactions/ignore_args_only.yml rename to test/system/test_interactions/expect_any_args.yml index 286b7c8..452bfb0 100644 --- a/test/system/test_interactions/ignore_args_only.yml +++ b/test/system/test_interactions/expect_any_args.yml @@ -1,8 +1,7 @@ --- :cmock: :plugins: - - 'ignore' - :ignore: :args_only + - 'expect_any_args' :systest: :types: | @@ -14,12 +13,17 @@ :source: :header: | int function(int a, int b, int c); + void func_b(int a); :code: | int function(int a, int b, int c) { bar(b); return foo(a) + foo(b) + foo(c); } + void func_b(int a) + { + bar(a); + } :tests: :common: | @@ -39,50 +43,50 @@ } - :pass: TRUE - :should: 'ignore foo() calls' + :should: 'ignore foo() call details' :code: | test() { bar_Expect(4); - foo_IgnoreAndReturn(10); - foo_IgnoreAndReturn(40); - foo_IgnoreAndReturn(80); + foo_ExpectAnyArgsAndReturn(10); + foo_ExpectAnyArgsAndReturn(40); + foo_ExpectAnyArgsAndReturn(80); TEST_ASSERT_EQUAL(130, function(3, 4, 3)); } - :pass: FALSE - :should: 'ignore foo() calls and notice if we called foo() more times than expected' + :should: 'ignore foo() call details and notice if we called foo() more times than expected' :code: | test() { bar_Expect(4); - foo_IgnoreAndReturn(20); - foo_IgnoreAndReturn(30); + foo_ExpectAnyArgsAndReturn(20); + foo_ExpectAnyArgsAndReturn(30); TEST_ASSERT_EQUAL(50, function(3, 4, 9)); } - :pass: FALSE - :should: 'ignore foo() calls and notice if we called foo() less times than expected' + :should: 'ignore foo() call details and notice if we called foo() less times than expected' :code: | test() { bar_Expect(4); - foo_IgnoreAndReturn(20); - foo_IgnoreAndReturn(10); - foo_IgnoreAndReturn(50); - foo_IgnoreAndReturn(60); + foo_ExpectAnyArgsAndReturn(20); + foo_ExpectAnyArgsAndReturn(10); + foo_ExpectAnyArgsAndReturn(50); + foo_ExpectAnyArgsAndReturn(60); TEST_ASSERT_EQUAL(70, function(3, 4, 9)); } - :pass: TRUE - :should: 'ignore bar() and foo() calls' + :should: 'ignore bar() and foo() call details' :code: | test() { - bar_Ignore(); - foo_IgnoreAndReturn(50); - foo_IgnoreAndReturn(50); - foo_IgnoreAndReturn(50); + bar_ExpectAnyArgs(); + foo_ExpectAnyArgsAndReturn(50); + foo_ExpectAnyArgsAndReturn(50); + foo_ExpectAnyArgsAndReturn(50); TEST_ASSERT_EQUAL(150, function(0, 0, 0)); } @@ -91,9 +95,9 @@ :code: | test() { - bar_Ignore(); - foo_IgnoreAndReturn(50); - foo_IgnoreAndReturn(50); + bar_ExpectAnyArgs(); + foo_ExpectAnyArgsAndReturn(50); + foo_ExpectAnyArgsAndReturn(50); foo_ExpectAndReturn(3, 50); TEST_ASSERT_EQUAL(150, function(1, 2, 3)); } @@ -103,10 +107,10 @@ :code: | test() { - bar_Ignore(); + bar_ExpectAnyArgs(); foo_ExpectAndReturn(1, 50); - foo_IgnoreAndReturn(50); - foo_IgnoreAndReturn(50); + foo_ExpectAnyArgsAndReturn(50); + foo_ExpectAnyArgsAndReturn(50); TEST_ASSERT_EQUAL(150, function(1, 2, 3)); } @@ -115,52 +119,76 @@ :code: | test() { - bar_Ignore(); + bar_ExpectAnyArgs(); foo_ExpectAndReturn(1, 50); - foo_IgnoreAndReturn(50); + foo_ExpectAnyArgsAndReturn(50); foo_ExpectAndReturn(3, 50); TEST_ASSERT_EQUAL(150, function(1, 2, 3)); } - # TODO: THIS IS WHAT PEOPLE EXPECT, BUT IS NOT BEING MET YET. - :pass: FALSE :should: 'be able to detect problems with an expect even when using ignores' :code: | test() { - bar_Ignore(); + bar_ExpectAnyArgs(); foo_ExpectAndReturn(1, 50); - foo_IgnoreAndReturn(50); + foo_ExpectAnyArgsAndReturn(50); foo_ExpectAndReturn(4, 50); TEST_ASSERT_EQUAL(150, function(1, 2, 3)); } + - :pass: TRUE + :should: 'be able to handle a lone ExpectAnyArg call' + :code: | + test() + { + bar_ExpectAnyArgs(); + func_b(1); + } + + - :pass: FALSE + :should: 'be able to handle a lone ExpectAnyArg call that does not get called' + :code: | + test() + { + bar_ExpectAnyArgs(); + } + + - :pass: FALSE + :should: 'be able to handle a missing ExpectAnyArg call' + :code: | + test() + { + func_b(1); + } + - :pass: TRUE :should: 'ignore foo() calls over multiple mock calls' :code: | test() { - bar_Ignore(); - foo_IgnoreAndReturn(50); - foo_IgnoreAndReturn(60); - foo_IgnoreAndReturn(70); + bar_ExpectAnyArgs(); + foo_ExpectAnyArgsAndReturn(50); + foo_ExpectAnyArgsAndReturn(60); + foo_ExpectAnyArgsAndReturn(70); TEST_ASSERT_EQUAL(180, function(0, 0, 0)); - bar_Ignore(); - foo_IgnoreAndReturn(30); - foo_IgnoreAndReturn(80); - foo_IgnoreAndReturn(10); + bar_ExpectAnyArgs(); + foo_ExpectAnyArgsAndReturn(30); + foo_ExpectAnyArgsAndReturn(80); + foo_ExpectAnyArgsAndReturn(10); TEST_ASSERT_EQUAL(120, function(0, 0, 0)); - bar_Ignore(); - foo_IgnoreAndReturn(70); - foo_IgnoreAndReturn(20); - foo_IgnoreAndReturn(20); + bar_ExpectAnyArgs(); + foo_ExpectAnyArgsAndReturn(70); + foo_ExpectAnyArgsAndReturn(20); + foo_ExpectAnyArgsAndReturn(20); TEST_ASSERT_EQUAL(110, function(0, 0, 0)); } - :pass: TRUE - :should: 'multiple cycles of expects still pass when ignores enabled' + :should: 'have multiple cycles of expects still pass when this plugin enabled' :code: | test() { @@ -184,7 +212,7 @@ } - :pass: FALSE - :should: 'multiple cycles of expects still fail when ignores enabled' + :should: 'have multiple cycles of expects still fail when this plugin enabled' :code: | test() {