mirror of
https://github.com/ThrowTheSwitch/CMock.git
synced 2026-09-22 18:17:26 +00:00
Fix handling of variable-length-arrays (fixed issue #479)
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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 == '&'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
Reference in New Issue
Block a user