From 5f74197056b2ecd7d9b433735adb8254a9ad7a5a Mon Sep 17 00:00:00 2001 From: Mark VanderVoord Date: Fri, 26 Jun 2026 10:15:07 -0400 Subject: [PATCH] The ReturnThrPtr plugin now works even when using Ignore (Fix #61, #225) --- lib/cmock_generator.rb | 1 + lib/cmock_generator_plugin_return_thru_ptr.rb | 38 +++++ .../return_thru_ptr_ignore.yml | 142 ++++++++++++++++++ test/unit/cmock_generator_main_test.rb | 4 + ...k_generator_plugin_return_thru_ptr_test.rb | 1 + 5 files changed, 186 insertions(+) create mode 100644 test/system/test_interactions/return_thru_ptr_ignore.yml diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index 516599a..eb6a3ca 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -346,6 +346,7 @@ class CMockGenerator 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_precheck_return_thru_ptr, function) 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" diff --git a/lib/cmock_generator_plugin_return_thru_ptr.rb b/lib/cmock_generator_plugin_return_thru_ptr.rb index e6f1bdb..b3bb08f 100644 --- a/lib/cmock_generator_plugin_return_thru_ptr.rb +++ b/lib/cmock_generator_plugin_return_thru_ptr.rb @@ -13,6 +13,8 @@ class CMockGeneratorPluginReturnThruPtr @utils = utils @priority = 9 @config = config + plugins = @config.plugins + @ignore_used = plugins.include?(:ignore) || plugins.include?(:ignore_stateless) end def ptr_to_const(arg_type) @@ -69,6 +71,25 @@ class CMockGeneratorPluginReturnThruPtr lines end + def mock_precheck_return_thru_ptr(function) + return '' unless @ignore_used + + lines = [] + function[:args].each do |arg| + arg_name = arg[:name] + next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?]) + + lines << " if (Mock.#{function[:name]}_IgnoreBool && cmock_call_instance != NULL &&\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n" + lines << " {\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n" + lines << " memcpy((void*)#{arg_name}, (const void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n" + lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n" + lines << " }\n" + end + lines + end + def mock_interfaces(function) lines = [] func_name = function[:name] @@ -80,6 +101,23 @@ class CMockGeneratorPluginReturnThruPtr lines << "{\n" lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \ "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" + if @ignore_used + lines << " if (Mock.#{func_name}_IgnoreBool &&\n" + lines << " (cmock_call_instance == NULL || cmock_call_instance->ReturnThruPtr_#{arg_name}_Used))\n" + lines << " {\n" + lines << " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n" + lines << " CMOCK_#{func_name}_CALL_INSTANCE* new_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" + lines << " UNITY_TEST_ASSERT_NOT_NULL(new_instance, cmock_line, CMockStringOutOfMemory);\n" + lines << " memset(new_instance, 0, sizeof(*new_instance));\n" + lines << " new_instance->LineNumber = cmock_line;\n" + unless function[:return][:void?] + lines << " if (cmock_call_instance != NULL)\n" + lines << " new_instance->ReturnVal = cmock_call_instance->ReturnVal;\n" + end + lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n" + lines << " cmock_call_instance = new_instance;\n" + lines << " }\n" + end lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp);\n" lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used = 1;\n" lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Val = #{arg_name};\n" diff --git a/test/system/test_interactions/return_thru_ptr_ignore.yml b/test/system/test_interactions/return_thru_ptr_ignore.yml new file mode 100644 index 0000000..ecfe3a9 --- /dev/null +++ b/test/system/test_interactions/return_thru_ptr_ignore.yml @@ -0,0 +1,142 @@ +# ========================================================================= +# CMock - Automatic Mock Generation for C +# ThrowTheSwitch.org +# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams +# SPDX-License-Identifier: MIT +# ========================================================================= + +--- +:cmock: + :mock_path: test/mocks + :mock_prefix: mock_ + :when_ptr: :smart + :plugins: + - :ignore + - :return_thru_ptr + +:systest: + :types: "" + :mockable: | + void ptr_ret_int(int *r); + int ptr_ret_int_rtn(int *r); + + :source: + :header: | + #include + #define lengthof(x) (sizeof(x)/sizeof((x)[0])) + + :code: | + + :tests: + :common: | + void setUp(void) {} + void tearDown(void) {} + + :units: + - :pass: TRUE + :should: "handle a single int* argument" + :code: | + test() + { + int r = 1; + int res = 4; + + ptr_ret_int_Expect(&r); + ptr_ret_int_ReturnThruPtr_r(&res); + + ptr_ret_int(&r); + TEST_ASSERT_EQUAL(4, r); + } + + - :pass: TRUE + :should: "ignore a call but still return arguments" + :code: | + test() + { + int r = 1; + int res = 4; + + ptr_ret_int_Ignore(); + ptr_ret_int_ReturnThruPtr_r(&res); + + ptr_ret_int(&r); + TEST_ASSERT_EQUAL(4, r); + } + + - :pass: TRUE + :should: "queue multiple return values in ignored calls" + :code: | + test() + { + int r = 1; + int res1 = 4; + int res2 = 8; + int res3 = 16; + + ptr_ret_int_Ignore(); + ptr_ret_int_ReturnThruPtr_r(&res1); + ptr_ret_int_ReturnThruPtr_r(&res2); + ptr_ret_int_ReturnThruPtr_r(&res3); + + ptr_ret_int(&r); + TEST_ASSERT_EQUAL(4, r); + ptr_ret_int(&r); + TEST_ASSERT_EQUAL(8, r); + ptr_ret_int(&r); + TEST_ASSERT_EQUAL(16, r); + + } + + - :pass: TRUE + :should: "return func and handle a single int* argument" + :code: | + test() + { + int r = 1; + int res = 4; + + ptr_ret_int_rtn_ExpectAndReturn(&r,1); + ptr_ret_int_rtn_ReturnThruPtr_r(&res); + + TEST_ASSERT_EQUAL_INT(1,ptr_ret_int_rtn(&r)); + TEST_ASSERT_EQUAL(4, r); + } + + - :pass: TRUE + :should: "ignore and return a call but still return arguments" + :code: | + test() + { + int r = 1; + int res = 4; + + ptr_ret_int_rtn_IgnoreAndReturn(1); + ptr_ret_int_rtn_ReturnThruPtr_r(&res); + + TEST_ASSERT_EQUAL_INT(1,ptr_ret_int_rtn(&r)); + TEST_ASSERT_EQUAL(4, r); + } + + - :pass: TRUE + :should: "queue multiple return values in ignore and return calls" + :code: | + test() + { + int r = 1; + int res1 = 4; + int res2 = 8; + int res3 = 16; + + ptr_ret_int_rtn_IgnoreAndReturn(1); + ptr_ret_int_rtn_ReturnThruPtr_r(&res1); + ptr_ret_int_rtn_ReturnThruPtr_r(&res2); + ptr_ret_int_rtn_ReturnThruPtr_r(&res3); + + TEST_ASSERT_EQUAL_INT(1,ptr_ret_int_rtn(&r)); + TEST_ASSERT_EQUAL(4, r); + TEST_ASSERT_EQUAL_INT(1,ptr_ret_int_rtn(&r)); + TEST_ASSERT_EQUAL(8, r); + TEST_ASSERT_EQUAL_INT(1,ptr_ret_int_rtn(&r)); + TEST_ASSERT_EQUAL(16, r); + + } diff --git a/test/unit/cmock_generator_main_test.rb b/test/unit/cmock_generator_main_test.rb index aeecded..b802054 100644 --- a/test/unit/cmock_generator_main_test.rb +++ b/test/unit/cmock_generator_main_test.rb @@ -539,6 +539,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do " return cmock_call_instance->ReturnVal;\n", "}\n\n" ] + @plugins.expect :run, "", [:mock_precheck_return_thru_ptr, function] @plugins.expect :run, [" uno"], [:mock_implementation_precheck, function] @plugins.expect :run, [" dos"," tres"], [:mock_implementation, function] @@ -577,6 +578,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do " return cmock_call_instance->ReturnVal;\n", "}\n\n" ] + @plugins.expect :run, "", [:mock_precheck_return_thru_ptr, function] @plugins.expect :run, [" uno"], [:mock_implementation_precheck, function] @plugins.expect :run, [" dos"," tres"], [:mock_implementation, function] @@ -618,6 +620,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do "}\n", "}\n\n", ] + @plugins.expect :run, "", [:mock_precheck_return_thru_ptr, function] @plugins.expect :run, [" uno"], [:mock_implementation_precheck, function] @plugins.expect :run, [" dos"," tres"], [:mock_implementation, function] @@ -656,6 +659,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do " return cmock_call_instance->ReturnVal;\n", "}\n\n" ] + @plugins.expect :run, "", [:mock_precheck_return_thru_ptr, function] @plugins.expect :run, [" uno"], [:mock_implementation_precheck, function] @plugins.expect :run, [" dos"," tres"], [:mock_implementation, function] diff --git a/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb b/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb index 65a309d..65b80e7 100644 --- a/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb +++ b/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb @@ -57,6 +57,7 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh :contains_ptr? => true } #no strict ordering + @config.expect :plugins, [] @cmock_generator_plugin_return_thru_ptr = CMockGeneratorPluginReturnThruPtr.new(@config, @utils) end