diff --git a/lib/cmock_generator_plugin_return_thru_ptr.rb b/lib/cmock_generator_plugin_return_thru_ptr.rb index 31d56a8..e6f1bdb 100644 --- a/lib/cmock_generator_plugin_return_thru_ptr.rb +++ b/lib/cmock_generator_plugin_return_thru_ptr.rb @@ -56,7 +56,12 @@ class CMockGeneratorPluginReturnThruPtr " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n" end lines << "#define #{function[:name]}_ReturnArrayThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_len)" - lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, (cmock_len * sizeof(*#{arg[:name]})))\n" + array_elem_size = if arg[:array_dims] && (arg[:type][-1] == '*') && !void_pointer?(arg[:type][0..-2]) + "sizeof(#{arg[:type][0..-2]})" + else + "sizeof(*#{arg[:name]})" + end + lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, (cmock_len * #{array_elem_size}))\n" lines << "#define #{function[:name]}_ReturnMemThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_size)" lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, (cmock_size))\n" lines << "void #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(UNITY_LINE_TYPE cmock_line, #{ptr_to_const(arg[:type])} #{arg[:name]}, size_t cmock_size);\n" diff --git a/lib/cmock_generator_utils.rb b/lib/cmock_generator_utils.rb index 25fc868..52c3ca5 100644 --- a/lib/cmock_generator_utils.rb +++ b/lib/cmock_generator_utils.rb @@ -37,7 +37,11 @@ class CMockGeneratorUtils end def self.arg_declaration(arg) - if arg[:array_dims] + if arg[:ptr_to_array?] && arg[:array_dims] + base_type = arg_type_with_const(arg).sub(/\*$/, '').strip + dims_str = arg[:array_dims].map { |d| "[#{d}]" }.join + "#{base_type} (*#{arg[:name]})#{dims_str}" + elsif arg[:array_dims] base_type = arg_type_with_const(arg).sub(/\*$/, '').strip dims_str = arg[:array_dims].map { |d| "[#{d}]" }.join "#{base_type} #{arg[:name]}#{dims_str}" @@ -146,6 +150,9 @@ class CMockGeneratorUtils arg_name = arg[:name] expected = "cmock_call_instance->Expected_#{arg_name}" ignore = "cmock_call_instance->IgnoreArg_#{arg_name}" + # Pointer-to-array args (e.g., char (*buf)[N]) always compare by memory contents + return [c_type, arg_name, expected, ignore, 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY', ''] if arg[:ptr_to_array?] + unity_func = if (arg[:ptr?]) && ((c_type =~ /\*\*/) || (@ptr_handling == :compare_ptr)) ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] else @@ -154,8 +161,19 @@ class CMockGeneratorUtils [c_type, arg_name, expected, ignore, unity_func[0], unity_func[1]] end + def ptr_to_array_elem_size(arg, c_type) + # For pointer-to-array args (e.g., char (*buf)[10]), the element size is sizeof(base_type[dims]) + if arg[:ptr_to_array?] && arg[:array_dims] + base_type = c_type.gsub(/\*+$/, '').strip + "sizeof(#{base_type}#{arg[:array_dims].map { |d| "[#{d}]" }.join})" + else + "sizeof(#{c_type.sub('*', '')})" + end + end + def code_verify_an_arg_expectation_with_no_arrays(function, arg) c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) + elem_size = ptr_to_array_elem_size(arg, c_type) lines = '' lines << " if (!#{ignore})\n" if @ignore_arg lines << " {\n" @@ -166,12 +184,12 @@ class CMockGeneratorUtils lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' if pre == '&' - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n" + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, #{elem_size}, cmock_line, CMockStringMismatch);\n" else lines << " if (#{pre}#{expected} == NULL)\n" lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" lines << " else\n" - lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch); }\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, #{elem_size}, cmock_line, CMockStringMismatch); }\n" end when /_ARRAY/ if pre == '&' @@ -192,6 +210,7 @@ class CMockGeneratorUtils def code_verify_an_arg_expectation_with_normal_arrays(function, arg) c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 + elem_size = ptr_to_array_elem_size(arg, c_type) lines = '' lines << " if (!#{ignore})\n" if @ignore_arg lines << " {\n" @@ -202,12 +221,12 @@ class CMockGeneratorUtils lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' if pre == '&' - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n" + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, #{elem_size}, cmock_line, CMockStringMismatch);\n" else lines << " if (#{pre}#{expected} == NULL)\n" lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n" lines << " else\n" - lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(#{pre}#{expected}, #{pre}#{arg_name}, #{elem_size}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" end when /_ARRAY/ if pre == '&' @@ -228,6 +247,7 @@ class CMockGeneratorUtils def code_verify_an_arg_expectation_with_smart_arrays(function, arg) c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 + elem_size = ptr_to_array_elem_size(arg, c_type) lines = '' lines << " if (!#{ignore})\n" if @ignore_arg lines << " {\n" @@ -238,13 +258,13 @@ class CMockGeneratorUtils lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' if pre == '&' - lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch);\n" + lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(#{pre}#{expected}, #{pre}#{arg_name}, #{elem_size}, #{depth_name}, cmock_line, CMockStringMismatch);\n" else lines << " if (#{pre}#{expected} == NULL)\n" lines << " { UNITY_TEST_ASSERT_NULL(#{arg_name}, cmock_line, CMockStringExpNULL); }\n" lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '') lines << " else\n" - lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(#{pre}#{expected}, #{pre}#{arg_name}, sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n" + lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(#{pre}#{expected}, #{pre}#{arg_name}, #{elem_size}, #{depth_name}, cmock_line, CMockStringMismatch); }\n" end when /_ARRAY/ if pre == '&' diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 4b3a253..ae418c9 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -417,7 +417,16 @@ class CMockHeaderParser dims end - def parse_args(arg_list, array_dims_by_name = {}) + def extract_ptr_to_array_dims(arg_list) + dims = {} + arg_list.scan(/\(\s*\*\s*(\w+)\s*\)((?:\s*\[[^\[\]]*\])+)/) do |name, all_dims| + dim_list = all_dims.scan(/\[([^\[\]]*)\]/).map { |d| d[0].strip } + dims[name] = dim_list + end + dims + end + + def parse_args(arg_list, array_dims_by_name = {}, ptr_to_array_dims_by_name = {}) args = [] arg_list.split(',').each do |arg| arg.strip! @@ -429,6 +438,13 @@ class CMockHeaderParser arg_info[:array_dims] = array_dims_by_name[arg_info[:name]] if array_dims_by_name.key?(arg_info[:name]) + # Handle pointer-to-array args: (*name)[dims] was rewritten to * name before clean_args + if ptr_to_array_dims_by_name.key?(arg_info[:name]) + arg_info[:array_dims] = ptr_to_array_dims_by_name[arg_info[:name]] + arg_info[:ptr_to_array?] = true + arg_info[:ptr?] = true # force true even for types like char* that divine_ptr would otherwise treat as strings + end + # in C, array arguments implicitly degrade to pointers # make the translation explicit here to simplify later logic if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?]) @@ -602,12 +618,18 @@ class CMockHeaderParser decl[:var_arg] = nil end + # Extract pointer-to-array parameters (e.g., char (*buffer)[10]) and rewrite for normal processing + ptr_to_array_dims = extract_ptr_to_array_dims(args) + ptr_to_array_dims.each_key do |name| + args.gsub!(/\(\s*\*\s*#{Regexp.escape(name)}\s*\)((?:\s*\[[^\[\]]*\])+)/, "* #{name}") + end + # Extract array dimensions before cleaning converts them to pointer notation array_dims_by_name = extract_array_dims(args) # parse out and clean up the remainder of the arguments args = clean_args(args, parse_project) - decl[:args] = parse_args(args, array_dims_by_name) + decl[:args] = parse_args(args, array_dims_by_name, ptr_to_array_dims) # Rebuild args_string using original array notation where applicable if args == 'void' decl[:args_string] = args @@ -618,7 +640,11 @@ class CMockHeaderParser base_type = arg[:type].sub(/\*$/, '').strip dims_str = arg[:array_dims].map { |d| "[#{d}]" }.join - arg_parts[i] = "#{base_type} #{arg[:name]}#{dims_str}" + arg_parts[i] = if arg[:ptr_to_array?] + "#{base_type} (*#{arg[:name]})#{dims_str}" + else + "#{base_type} #{arg[:name]}#{dims_str}" + end end decl[:args_string] = arg_parts.join(', ') end diff --git a/test/system/test_interactions/array_handling.yml b/test/system/test_interactions/array_handling.yml index 31b1dc9..c01a0a2 100644 --- a/test/system/test_interactions/array_handling.yml +++ b/test/system/test_interactions/array_handling.yml @@ -47,6 +47,8 @@ void process_samples(SAMPLE_U samples[], int count); void fill_matrix(int matrix[13][4]); void transform_grid(int grid[][4], int rows); + void write_buffer(char (*dest)[10]); + void read_buffers(int count, char (*buffers)[8]); :source: :header: | @@ -57,6 +59,8 @@ void function_e(void); void function_f(void); void function_g(void); + void function_h(void); + void function_i(void); :code: | void function_a(void) @@ -108,6 +112,22 @@ transform_grid(grid, 3); } + void function_h(void) + { + char buf[10] = {1,2,3,4,5,6,7,8,9,10}; + write_buffer(&buf); + } + + void function_i(void) + { + char bufs[3][8]; + int i, j; + for (i = 0; i < 3; i++) + for (j = 0; j < 8; j++) + bufs[i][j] = (char)(i * 8 + j + 1); + read_buffers((int)3, bufs); + } + :tests: :common: | void setUp(void) {} @@ -516,4 +536,66 @@ function_g(); } + - :pass: TRUE + :should: 'pass when pointer-to-array argument contents match' + :code: | + test() + { + char expected[10] = {1,2,3,4,5,6,7,8,9,10}; + write_buffer_Expect(&expected); + + function_h(); + } + + - :pass: FALSE + :should: 'detect mismatch in pointer-to-array argument contents' + :code: | + test() + { + char expected[10] = {1,2,3,4,5,6,7,8,9,99}; + write_buffer_Expect(&expected); + + function_h(); + } + + - :pass: TRUE + :should: 'pass when passing null to pointer-to-array parameter' + :code: | + test() + { + write_buffer_Expect(NULL); + write_buffer(NULL); + } + + - :pass: TRUE + :should: 'compare multiple pointer-to-array elements via ExpectWithArray and pass' + :code: | + test() + { + char expected[3][8]; + int i, j; + for (i = 0; i < 3; i++) + for (j = 0; j < 8; j++) + expected[i][j] = (char)(i * 8 + j + 1); + read_buffers_ExpectWithArray(3, expected, 3); + + function_i(); + } + + - :pass: FALSE + :should: 'detect mismatch when comparing multiple pointer-to-array elements' + :code: | + test() + { + char expected[3][8]; + int i, j; + for (i = 0; i < 3; i++) + for (j = 0; j < 8; j++) + expected[i][j] = (char)(i * 8 + j + 1); + expected[1][3] = 99; + read_buffers_ExpectWithArray(3, expected, 3); + + function_i(); + } + ...