diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index 088c322..1d35476 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -8,20 +8,22 @@ class CMockConfig CMockDefaultOptions = { - :mock_path => 'mocks', - :mock_prefix => 'Mock', - :plugins => [], - :includes => [], - :attributes => ['__ramfunc', '__irq', '__fiq', 'register', 'extern'], + :mock_path => 'mocks', + :mock_prefix => 'Mock', + :plugins => [], + :includes => [], + :attributes => ['__ramfunc', '__irq', '__fiq', 'register', 'extern'], :enforce_strict_ordering => false, - :cexception_include => nil, - :unity_helper => false, - :treat_as => {}, - :treat_as_void => [], - :memcmp_if_unknown => true, - :when_no_prototypes => :warn, #the options being :ignore, :warn, or :error - :when_ptr =>:compare_data, #the options being :compare_ptr, :compare_data, or :smart - :verbosity => 2, #0 errors only, #1 warnings and errors, #2 normal info, #3 verbose + :cexception_include => nil, + :unity_helper => false, + :treat_as => {}, + :treat_as_void => [], + :memcmp_if_unknown => true, + :when_no_prototypes => :warn, #the options being :ignore, :warn, or :error + :when_ptr => :compare_data, #the options being :compare_ptr, :compare_data, or :smart + :verbosity => 2, #the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose + :treat_externs => :exclude, #the options being :include or :exclude + :ignore => :args_and_calls, #the options being :args_and_calls or :args_only } def initialize(options=nil) diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index cbe415d..4cf9339 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -173,6 +173,9 @@ class CMockGenerator file << @plugins.run(:mock_implementation_precheck, function) file << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, \"Function '#{function[:name]}' called more times than expected\");\n" file << " cmock_line = cmock_call_instance->LineNumber;\n" + if (@ordered) + file << " UNITY_TEST_ASSERT((cmock_call_instance->CallOrder == ++GlobalVerifyOrder), cmock_line, \"Out of order function calls. Function '#{function[:name]}'\");\n" + end file << @plugins.run(:mock_implementation, function) file << " return cmock_call_instance->ReturnVal;\n" unless (function[:return][:void?]) file << "}\n\n" diff --git a/lib/cmock_generator_plugin_expect.rb b/lib/cmock_generator_plugin_expect.rb index ce395c9..9982fe2 100644 --- a/lib/cmock_generator_plugin_expect.rb +++ b/lib/cmock_generator_plugin_expect.rb @@ -50,9 +50,6 @@ class CMockGeneratorPluginExpect def mock_implementation(function) lines = "" - if (@ordered) - lines << " UNITY_TEST_ASSERT((cmock_call_instance->CallOrder == ++GlobalVerifyOrder), cmock_line, \"Out of order function calls. Function '#{function[:name]}'\");\n" - end function[:args].each do |arg| lines << @utils.code_verify_an_arg_expectation(function, arg) end diff --git a/lib/cmock_generator_plugin_ignore.rb b/lib/cmock_generator_plugin_ignore.rb index b072d57..2f74295 100644 --- a/lib/cmock_generator_plugin_ignore.rb +++ b/lib/cmock_generator_plugin_ignore.rb @@ -11,6 +11,11 @@ class CMockGeneratorPluginIgnore def initialize(config, utils) @config = config + if (@config.ignore == :args_and_calls) + alias :mock_implementation_precheck :mock_implementation_for_ignores + else + alias :mock_implementation :mock_implementation_for_ignores + end @utils = utils @priority = 2 end @@ -33,7 +38,7 @@ class CMockGeneratorPluginIgnore end end - def mock_implementation_precheck(function) + def mock_implementation_for_ignores(function) lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" if (function[:return][:void?]) lines << " return;\n }\n" diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 529f21d..2c9059d 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -6,7 +6,7 @@ class CMockHeaderParser - attr_accessor :funcs, :c_attributes, :treat_as_void + attr_accessor :funcs, :c_attributes, :treat_as_void, :treat_externs def initialize(cfg) @funcs = [] @@ -17,6 +17,7 @@ class CMockHeaderParser @when_no_prototypes = cfg.when_no_prototypes @local_as_void = @treat_as_void @verbosity = cfg.verbosity + @treat_externs = cfg.treat_externs end def parse(name, source) @@ -90,9 +91,13 @@ class CMockHeaderParser #split lines on semicolons and remove things that are obviously not what we are looking for src_lines = source.split(/\s*;\s*/) - src_lines.delete_if {|line| !(line =~ /\(\s*\*(?:.*\[\d*\])??\s*\)/).nil?} #remove function pointer arrays - src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern|inline)\s+/).nil?} #remove inline and extern functions - src_lines.delete_if {|line| line.strip.length == 0} # remove blank lines + src_lines.delete_if {|line| line.strip.length == 0} # remove blank lines + src_lines.delete_if {|line| !(line =~ /\(\s*\*(?:.*\[\d*\])??\s*\)/).nil?} #remove function pointer arrays + if (@treat_externs == :include) + src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?} #remove inline functions + else + src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern|inline)\s+/).nil?} #remove inline and extern functions + end end def parse_functions(source) diff --git a/test/system/test_interactions/all_plugins_but_other_limits.yml b/test/system/test_interactions/all_plugins_but_other_limits.yml new file mode 100644 index 0000000..ac8553a --- /dev/null +++ b/test/system/test_interactions/all_plugins_but_other_limits.yml @@ -0,0 +1,340 @@ +--- +#this test is different than all_plugins_coexist primarily because of these options +:cmock: + :enforce_strict_ordering: 1 + :treat_externs: :include + :ignore: :args_only + :plugins: + - :array + - :cexception + - :ignore + - :callback + +:systest: + :types: | + typedef struct _POINT_T { + int x; + int y; + } POINT_T; + + :mockable: | + #include "CException.h" + void foo(POINT_T* a); + POINT_T* bar(void); + void fooa(POINT_T a[]); + void foos(const char * a); + extern const char * bars(void); + void no_pointers(int a, char* b); + int mixed(int a, int* b, int c); + + :source: + :header: | + #include "CException.h" + void function_a(void); + void function_b(void); + void function_c(void); + int function_d(void); + void function_e(void); + + :code: | + 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' + :code: | + test() + { + 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: | + test() + { + 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: | + test() + { + 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: | + test() + { + POINT_T pt = {1, 2}; + 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: | + test() + { + POINT_T pt = {1, 2}; + 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: | + test() + { + POINT_T pt = {1, 2}; + 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: | + test() + { + POINT_T pt[] = {{1, 2}, {3, 4}, {5, 6}}; + 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: | + test() + { + POINT_T pt[] = {{1, 2}, {3, 4}, {5, 6}}; + POINT_T ex[] = {{1, 2}, {3, 4}, {5, 9}}; + bar_ExpectAndReturn(pt); + foo_ExpectWithArray(ex, 3); + + function_a(); + } + + - :pass: TRUE + :should: 'handle the situation where we pass single array element with expect' + :code: | + test() + { + POINT_T pt = {1, 2}; + POINT_T ex = {1, 2}; + bar_ExpectAndReturn(&pt); + fooa_Expect(&ex); + + function_b(); + } + + - :pass: TRUE + :should: 'handle standard c string as null terminated on not do crappy memory compares of a byte, passing' + :code: | + test() + { + 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 on not do crappy memory compares of a byte, finding failures' + :code: | + test() + { + bars_ExpectAndReturn("This is a silly string"); + foos_Expect("This is a wacky string"); + + function_c(); + } + + - :pass: TRUE + :should: 'handle creating array expects when we have mixed arguments for single object' + :code: | + test() + { + int expect_list[] = { 1, 9 }; + no_pointers_Expect(1, "silly"); + mixed_ExpectAndReturn(6, expect_list, 7, 13); + + TEST_ASSERT_EQUAL(13, function_d()); + } + + - :pass: FALSE + :should: 'handle creating array expects when we have mixed arguments and handle failures for single object' + :code: | + test() + { + int expect_list[] = { 9, 1 }; + no_pointers_Expect(1, "silly"); + mixed_ExpectAndReturn(6, expect_list, 7, 13); + + TEST_ASSERT_EQUAL(13, function_d()); + } + + - :pass: TRUE + :should: 'handle creating array expects when we have mixed arguments for multiple objects' + :code: | + test() + { + 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()); + } + + - :pass: FALSE + :should: 'handle creating array expects when we have mixed arguments and handle failures for multiple objects' + :code: | + test() + { + 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()); + } + + - :pass: TRUE + :should: 'handle an exception being caught' + :code: | + test() + { + bars_ExpectAndReturn("This is a\0 silly string"); + foos_ExpectAndThrow("This is a\0 wacky string", 55); + foos_Expect("err"); + + function_c(); + } + + - :pass: FALSE + :should: 'handle an exception being caught but still catch following errors' + :code: | + test() + { + bars_ExpectAndReturn("This is a\0 silly string"); + foos_ExpectAndThrow("This is a\0 wacky string", 55); + foos_Expect("wrong error"); + + function_c(); + } + + - :pass: FALSE + :should: 'fail strict ordering problems even though we would otherwise have passed' + :code: | + test() + { + 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: 'that we can properly ignore last function but the other will work properly' + :code: | + test() + { + int expect_list[] = { 1, 2, 3, 4, 6 }; + mixed_ExpectWithArrayAndReturn(6, expect_list, 4, 7, 13); + no_pointers_Ignore(); + + TEST_ASSERT_EQUAL(13, function_d()); + } + + - :pass: TRUE + :should: 'that we can properly ignore first function but the other will work properly' + :code: | + test() + { + mixed_IgnoreAndReturn(13); + no_pointers_Expect(1, "silly"); + + TEST_ASSERT_EQUAL(13, function_d()); + } + + - :pass: TRUE + :should: 'that we are ok if we ignore a call each because we are counting calls' + :code: | + test() + { + foos_Ignore(); + foos_Ignore(); + foos_Ignore(); + + function_e(); + } + + - :pass: TRUE + :should: 'that we fail if we do not ignore a call once because we are counting calls' + :code: | + test() + { + foos_Ignore(); + foos_Ignore(); + + function_e(); + } + +... diff --git a/test/system/test_interactions/all_plugins_coexist.yml b/test/system/test_interactions/all_plugins_coexist.yml index 6b13115..cd83201 100644 --- a/test/system/test_interactions/all_plugins_coexist.yml +++ b/test/system/test_interactions/all_plugins_coexist.yml @@ -31,6 +31,7 @@ void function_b(void); void function_c(void); int function_d(void); + void function_e(void); :code: | void function_a(void) @@ -55,6 +56,12 @@ return mixed(6, test_list, 7); } + void function_e(void) { + foos("Hello"); + foos("Tuna"); + foos("Oranges"); + } + :tests: :common: | #include "CException.h" @@ -304,4 +311,14 @@ TEST_ASSERT_EQUAL(13, function_d()); } + - :pass: TRUE + :should: 'that we just have to ignore a call once because we are not counting calls' + :code: | + test() + { + foos_Ignore(); + + function_e(); + } + ... diff --git a/test/unit/cmock_generator_plugin_expect_test.rb b/test/unit/cmock_generator_plugin_expect_test.rb index e905221..cafbf81 100644 --- a/test/unit/cmock_generator_plugin_expect_test.rb +++ b/test/unit/cmock_generator_plugin_expect_test.rb @@ -11,17 +11,19 @@ class CMockGeneratorPluginExpectTest < Test::Unit::TestCase def setup create_mocks :config, :utils - #no strict ordering + #no strict ordering and args_and_calls @config.expect.when_ptr.returns(:compare_data) @config.expect.enforce_strict_ordering.returns(false) @config.stubs!(:respond_to?).returns(true) + # @config.expect.ignore.returns(:args_and_calls) @utils.expect.helpers.returns({}) @cmock_generator_plugin_expect = CMockGeneratorPluginExpect.new(@config, @utils) - #strict ordering + #strict ordering and args_only @config.expect.when_ptr.returns(:compare_data) @config.expect.enforce_strict_ordering.returns(true) @config.stubs!(:respond_to?).returns(true) + # @config.expect.ignore.returns(:args_only) @utils.expect.helpers.returns({}) @cmock_generator_plugin_expect_strict = CMockGeneratorPluginExpect.new(@config, @utils) end @@ -118,7 +120,7 @@ class CMockGeneratorPluginExpectTest < Test::Unit::TestCase should "add mock function implementation using ordering if needed" do function = {:name => "Apple", :args => [], :return => test_return[:void]} - expected = " UNITY_TEST_ASSERT((cmock_call_instance->CallOrder == ++GlobalVerifyOrder), cmock_line, \"Out of order function calls. Function 'Apple'\");\n" + expected = "" @cmock_generator_plugin_expect.ordered = true returned = @cmock_generator_plugin_expect.mock_implementation(function) assert_equal(expected, returned) @@ -127,7 +129,7 @@ class CMockGeneratorPluginExpectTest < Test::Unit::TestCase should "add mock function implementation for functions of style 'void func(int worm)' and strict ordering" do function = {:name => "Apple", :args => [{ :type => "int", :name => "worm" }], :return => test_return[:void]} @utils.expect.code_verify_an_arg_expectation(function, function[:args][0]).returns("mocked_retval_0") - expected = " UNITY_TEST_ASSERT((cmock_call_instance->CallOrder == ++GlobalVerifyOrder), cmock_line, \"Out of order function calls. Function 'Apple'\");\nmocked_retval_0" + expected = "mocked_retval_0" @cmock_generator_plugin_expect.ordered = true returned = @cmock_generator_plugin_expect_strict.mock_implementation(function) assert_equal(expected, returned) diff --git a/test/unit/cmock_generator_plugin_ignore_test.rb b/test/unit/cmock_generator_plugin_ignore_test.rb index 641e67c..24d4524 100644 --- a/test/unit/cmock_generator_plugin_ignore_test.rb +++ b/test/unit/cmock_generator_plugin_ignore_test.rb @@ -10,8 +10,12 @@ require 'cmock_generator_plugin_ignore' class CMockGeneratorPluginIgnoreTest < Test::Unit::TestCase def setup create_mocks :config, :utils + @config.expect.ignore.returns(:args_and_calls) @config.stubs!(:respond_to?).returns(true) @cmock_generator_plugin_ignore = CMockGeneratorPluginIgnore.new(@config, @utils) + + @config.expect.ignore.returns(:args_only) + @cmock_generator_plugin_ignore_just_args = CMockGeneratorPluginIgnore.new(@config, @utils) end def teardown @@ -49,7 +53,11 @@ class CMockGeneratorPluginIgnoreTest < Test::Unit::TestCase assert_equal(expected, returned) end - should "add required code to implementation with void function" do + should "not add code to implementation (when :args_and_calls)" do + assert(! @cmock_generator_plugin_ignore.methods.include?(:mock_implementation)) + end + + should "add required code to implementation precheck with void function (when :args_and_calls)" do function = {:name => "Mold", :args_string => "void", :return => test_return[:void]} expected = [" if (Mock.Mold_IgnoreBool)\n", " {\n", @@ -60,7 +68,7 @@ class CMockGeneratorPluginIgnoreTest < Test::Unit::TestCase assert_equal(expected, returned) end - should "add required code to implementation with return functions" do + should "add required code to implementation precheck with return functions (when :args_and_calls)" do function = {:name => "Fungus", :args_string => "void", :return => test_return[:int]} retval = test_return[:int].merge({ :name => "cmock_call_instance->ReturnVal"}) @utils.expect.code_assign_argument_quickly("Mock.Fungus_FinalReturn", retval).returns(' mock_retval_0') @@ -76,6 +84,37 @@ class CMockGeneratorPluginIgnoreTest < Test::Unit::TestCase assert_equal(expected, returned) end + should "not add code to implementation prefix (when :args_only)" do + assert(! @cmock_generator_plugin_ignore_just_args.methods.include?(:mock_implementation_precheck)) + end + + should "add required code to implementation with void function (when :args_only)" do + function = {:name => "Mold", :args_string => "void", :return => test_return[:void]} + expected = [" if (Mock.Mold_IgnoreBool)\n", + " {\n", + " return;\n", + " }\n" + ].join + returned = @cmock_generator_plugin_ignore_just_args.mock_implementation(function) + assert_equal(expected, returned) + end + + should "add required code to implementation with return functions (when :args_only)" do + function = {:name => "Fungus", :args_string => "void", :return => test_return[:int]} + retval = test_return[:int].merge({ :name => "cmock_call_instance->ReturnVal"}) + @utils.expect.code_assign_argument_quickly("Mock.Fungus_FinalReturn", retval).returns(' mock_retval_0') + expected = [" if (Mock.Fungus_IgnoreBool)\n", + " {\n", + " if (cmock_call_instance == NULL)\n", + " return Mock.Fungus_FinalReturn;\n", + " mock_retval_0", + " return cmock_call_instance->ReturnVal;\n", + " }\n" + ].join + returned = @cmock_generator_plugin_ignore_just_args.mock_implementation(function) + assert_equal(expected, returned) + end + should "add a new mock interface for ignoring when function had no return value" do function = {:name => "Slime", :args => [], :args_string => "void", :return => test_return[:void]} expected = ["void Slime_CMockIgnore(UNITY_LINE_TYPE cmock_line)\n", diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 446e128..0751d40 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -19,6 +19,7 @@ class CMockHeaderParserTest < Test::Unit::TestCase @config.expect.treat_as.returns({ "BANJOS" => "INT", "TUBAS" => "HEX16"} ) @config.expect.when_no_prototypes.returns(:error) @config.expect.verbosity.returns(1) + @config.expect.treat_externs.returns(:exclude) @parser = CMockHeaderParser.new(@config) end @@ -215,7 +216,6 @@ class CMockHeaderParserTest < Test::Unit::TestCase @parser.import_source(source).map!{|s|s.strip}) end - should "remove externed and inline functions" do source = " extern uint32 foobar(unsigned int);\n" + @@ -234,6 +234,28 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end + + should "remove just inline functions if externs to be included" do + source = + " extern uint32 foobar(unsigned int);\n" + + "uint32 extern_name_func(unsigned int);\n" + + "uint32 funcinline(unsigned int);\n" + + "extern void bar(unsigned int);\n" + + "inline void bar(unsigned int);\n" + + "extern\n" + + "void kinda_ugly_on_the_next_line(unsigned int);\n" + + expected = + [ "extern uint32 foobar(unsigned int)", + "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)", + "extern void bar(unsigned int)", + "extern void kinda_ugly_on_the_next_line(unsigned int)" + ] + + @parser.treat_externs = :include + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) + end should "remove defines" do diff --git a/test/unit/cmock_plugin_manager_test.rb b/test/unit/cmock_plugin_manager_test.rb index eb972b7..12a45e0 100644 --- a/test/unit/cmock_plugin_manager_test.rb +++ b/test/unit/cmock_plugin_manager_test.rb @@ -13,6 +13,7 @@ class CMockPluginManagerTest < Test::Unit::TestCase @config.stubs!(:respond_to?).returns(true) @config.stubs!(:when_ptr).returns(:compare_data) @config.stubs!(:enforce_strict_ordering).returns(false) + @config.stubs!(:ignore).returns(:args_and_calls) end def teardown