mirror of
https://github.com/ThrowTheSwitch/CMock.git
synced 2026-06-28 00:10:28 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df5ba4f2fc | |||
| d141b59324 | |||
| b992f1f2e9 | |||
| e851a39d58 | |||
| dc34a9000c | |||
| 55dd1f1ce5 | |||
| 9e8d1f93ef | |||
| 783bbde34d | |||
| 92debabbf0 | |||
| d20d18e75c | |||
| 5f74197056 | |||
| 6e048b07ff | |||
| d482f56066 | |||
| 1ef72ca854 | |||
| 85058d1407 | |||
| 411f6852f9 | |||
| 383c43246c |
@@ -378,6 +378,23 @@ generate a skeleton instead:
|
||||
ruby cmock.rb --skeleton ../create/c/for/this.h
|
||||
```
|
||||
|
||||
Using CMock Without Ceedling
|
||||
----------------------------
|
||||
|
||||
CMock depends on the Unity test framework, but it does not *require* Ceedling. You can use the
|
||||
generated mocks directly with the Unity test framework in whatever build system you prefer. One
|
||||
important thing to remember when doing this is that you will need to call the `_Init` function
|
||||
for each of your mocks BEFORE the tests and the `_Verify` function for each mock AFTER each test.
|
||||
This allows CMock to perform all of its internal accounting. If you're running into problems where
|
||||
some errors aren't getting caught, this is likely what you are missing.
|
||||
|
||||
There are many ways to accomplish this. Any is valid:
|
||||
|
||||
- These actions can be performed as part of `setUp` and `tearDown` in each test file
|
||||
- You can hand-write your own RUN_TEST macro. If so, protect `_Verify` calls in `TEST_PROTECT`
|
||||
- You can use Unity's test runner generator and it will automatically take care of this for you.
|
||||
- You can use Ceedling and it will automatically take care of this for you.
|
||||
|
||||
Config Options:
|
||||
---------------
|
||||
|
||||
@@ -864,6 +881,29 @@ based on other settings, particularly Unity's settings.
|
||||
This needs to be something big enough to point anywhere in Cmock's
|
||||
memory space... usually it's a size_t.
|
||||
|
||||
* `CMOCK_MEMCPY`
|
||||
The memory-copy function used by CMock's internals and the generated mocks.
|
||||
It defaults to `memcpy` from `<string.h>`. Override alongside `CMOCK_MEMSET`
|
||||
to supply a custom implementation on targets where the standard library is
|
||||
unavailable or undesirable:
|
||||
|
||||
```c
|
||||
#define CMOCK_MEMCPY(dst, src, size) my_memcpy(dst, src, size)
|
||||
```
|
||||
|
||||
* `CMOCK_MEMSET`
|
||||
The memory-set function used by CMock's internals to zero-initialize
|
||||
allocated blocks. It defaults to `memset` from `<string.h>`. Override
|
||||
alongside `CMOCK_MEMCPY` to keep CMock fully independent of the standard
|
||||
library:
|
||||
|
||||
```c
|
||||
#define CMOCK_MEMSET(dst, val, size) my_memset(dst, val, size)
|
||||
```
|
||||
|
||||
If both `CMOCK_MEMCPY` and `CMOCK_MEMSET` are defined before including
|
||||
`cmock.h`, CMock will not pull in `<string.h>` at all.
|
||||
|
||||
Other Tips
|
||||
==========
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void testShouldInitializeTemeratureToInvalidValue(void)
|
||||
void testShouldInitializeTemperatureToInvalidValue(void)
|
||||
{
|
||||
TemperatureFilter_Init();
|
||||
TEST_ASSERT_FLOAT_WITHIN(0.0001f, -INFINITY, TemperatureFilter_GetTemperatureInCelcius());
|
||||
|
||||
@@ -16,6 +16,17 @@ class CMockFileWriter
|
||||
require 'fileutils'
|
||||
FileUtils.mkdir_p "#{@config.mock_path}/" unless Dir.exist?("#{@config.mock_path}/")
|
||||
FileUtils.mkdir_p "#{@config.mock_path}/#{"#{subdir}/" if subdir}" if subdir && !Dir.exist?("#{@config.mock_path}/#{"#{subdir}/" if subdir}")
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to create mock output directory: #{e.message}. Check :mock_path ('#{@config.mock_path}') configuration."
|
||||
end
|
||||
|
||||
def create_skeleton_subdir(subdir)
|
||||
require 'fileutils'
|
||||
base = effective_skeleton_path
|
||||
FileUtils.mkdir_p base
|
||||
FileUtils.mkdir_p "#{base}/#{subdir}" if subdir
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to create skeleton output directory: #{e.message}. Check :skeleton_path ('#{base}') configuration."
|
||||
end
|
||||
|
||||
def create_file(filename, subdir)
|
||||
@@ -27,6 +38,27 @@ class CMockFileWriter
|
||||
yield(file, filename)
|
||||
end
|
||||
update_file(full_file_name_done, full_file_name_temp)
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to write mock file '#{full_file_name_done}': #{e.message}. Check :mock_path ('#{@config.mock_path}') and :subdir ('#{subdir}') configuration."
|
||||
end
|
||||
|
||||
def create_skeleton_file(filename, subdir)
|
||||
raise "Where's the block of data to create?" unless block_given?
|
||||
|
||||
base = effective_skeleton_path
|
||||
full_file_name_temp = "#{base}/#{"#{subdir}/" if subdir}#{filename}.new"
|
||||
full_file_name_done = "#{base}/#{"#{subdir}/" if subdir}#{filename}"
|
||||
File.open(full_file_name_temp, 'w') do |file|
|
||||
yield(file, filename)
|
||||
end
|
||||
update_file(full_file_name_done, full_file_name_temp)
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to write skeleton file '#{full_file_name_done}': #{e.message}. Check :skeleton_path ('#{base}') and :subdir ('#{subdir}') configuration."
|
||||
end
|
||||
|
||||
def skeleton_file_path(filename, subdir)
|
||||
base = effective_skeleton_path
|
||||
"#{base}/#{"#{subdir}/" if subdir}#{filename}"
|
||||
end
|
||||
|
||||
def append_file(filename, subdir)
|
||||
@@ -40,6 +72,11 @@ class CMockFileWriter
|
||||
|
||||
private ###################################
|
||||
|
||||
def effective_skeleton_path
|
||||
path = @config.skeleton_path
|
||||
path.nil? || path.empty? ? @config.mock_path : path
|
||||
end
|
||||
|
||||
def update_file(dest, src)
|
||||
require 'fileutils'
|
||||
FileUtils.rm(dest, :force => true)
|
||||
|
||||
+28
-20
@@ -85,6 +85,7 @@ class CMockGenerator
|
||||
:skeleton => true
|
||||
}
|
||||
|
||||
@file_writer.create_skeleton_subdir(@subdir)
|
||||
create_skeleton_source_file(mock_project)
|
||||
end
|
||||
|
||||
@@ -133,9 +134,9 @@ class CMockGenerator
|
||||
end
|
||||
|
||||
def create_skeleton_source_file(mock_project)
|
||||
filename = "#{@config.mock_path}/#{"#{@subdir}/" if @subdir}#{mock_project[:module_name]}.c"
|
||||
filename = @file_writer.skeleton_file_path("#{mock_project[:module_name]}.c", @subdir)
|
||||
existing = File.exist?(filename) ? File.read(filename) : ''
|
||||
@file_writer.create_file("#{mock_project[:module_name]}.c", @subdir) do |file, fullname|
|
||||
@file_writer.create_skeleton_file("#{mock_project[:module_name]}.c", @subdir) do |file, fullname|
|
||||
blank_project = mock_project.clone
|
||||
blank_project[:parsed_stuff] = { :functions => [] }
|
||||
if existing.empty?
|
||||
@@ -210,24 +211,30 @@ class CMockGenerator
|
||||
|
||||
def create_source_header_section(file, filename, mock_project)
|
||||
header_file = (mock_project[:folder] || '') + filename.sub(/.*\K\.c/, mock_project[:module_ext])
|
||||
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty?
|
||||
file << "#include <string.h>\n"
|
||||
file << "#include <stdlib.h>\n"
|
||||
unless @exclude_setjmp_h
|
||||
file << "#include <setjmp.h>\n"
|
||||
end
|
||||
file << "#include \"cmock.h\"\n"
|
||||
@includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "#include \"#{header_file}\"\n"
|
||||
@includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "\n"
|
||||
strs = []
|
||||
mock_project[:parsed_stuff][:functions].each do |func|
|
||||
strs << func[:name]
|
||||
func[:args].each { |arg| strs << arg[:name] }
|
||||
end
|
||||
strs.uniq.sort.each do |str|
|
||||
file << "static const char* CMockString_#{str} = \"#{str}\";\n"
|
||||
if mock_project[:skeleton]
|
||||
@includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "#include \"#{header_file}\"\n"
|
||||
@includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
|
||||
else
|
||||
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty?
|
||||
file << "#include <string.h>\n"
|
||||
file << "#include <stdlib.h>\n"
|
||||
unless @exclude_setjmp_h
|
||||
file << "#include <setjmp.h>\n"
|
||||
end
|
||||
file << "#include \"cmock.h\"\n"
|
||||
@includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "#include \"#{header_file}\"\n"
|
||||
@includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "\n"
|
||||
strs = []
|
||||
mock_project[:parsed_stuff][:functions].each do |func|
|
||||
strs << func[:name]
|
||||
func[:args].each { |arg| strs << arg[:name] }
|
||||
end
|
||||
strs.uniq.sort.each do |str|
|
||||
file << "static const char* CMockString_#{str} = \"#{str}\";\n"
|
||||
end
|
||||
end
|
||||
file << "\n"
|
||||
end
|
||||
@@ -339,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"
|
||||
|
||||
@@ -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 << " CMOCK_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"
|
||||
@@ -98,7 +136,7 @@ class CMockGeneratorPluginReturnThruPtr
|
||||
lines << " if (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_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
|
||||
|
||||
@@ -251,14 +251,27 @@ class CMockHeaderParser
|
||||
source.gsub!(/^\s*#.*/, '')
|
||||
|
||||
# enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them
|
||||
# pre-collapse nested brace pairs so that structs containing nested structs/unions are removed as a unit below
|
||||
source = remove_nested_pairs_of_braces(source) unless cpp
|
||||
# forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes
|
||||
source.gsub!(/^[\w\s]*struct[^;{}()]+;/m, '') # remove forward declared structs
|
||||
source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^}]+\}[\w\s*,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces
|
||||
source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s()]*\{[^}]+\}[\w\s*,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces
|
||||
# remove problem keywords
|
||||
source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2')
|
||||
source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp
|
||||
|
||||
source.gsub!(/\s*=\s*['"a-zA-Z0-9_.]+\s*/, '') # remove default value statements from argument lists
|
||||
|
||||
# strip macro decorator patterns that cannot be C function prototypes.
|
||||
# must run after default-value removal so "= \"str\"" doesn't trigger string detection.
|
||||
# neutralize string literals (never valid in C prototype args), eliminating any parentheses
|
||||
# inside string content (e.g. "msg()") that would fool subsequent brace-matching.
|
||||
source.gsub!(/"[^"]*"/, '""')
|
||||
# strip WORD("") -- any call with a string literal arg cannot be a C function prototype
|
||||
source.gsub!(/\b\w+\s*\([^)]*""[^)]*\)/, '')
|
||||
# strip WORD(N...) -- any call whose first arg starts with a digit cannot be a C prototype
|
||||
source.gsub!(/\b\w+\s*\(\s*\d[^)]*\)/, '')
|
||||
|
||||
source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements
|
||||
source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric
|
||||
source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/, '\1') unless @c_strippables.empty? # remove known attributes slated to be stripped
|
||||
@@ -554,6 +567,10 @@ class CMockHeaderParser
|
||||
# pull asterisks away from arg to place asterisks with type (where they belong)
|
||||
arg_list.gsub!(/\*(\w)/, '* \1')
|
||||
|
||||
# normalize parenthesized pointer arguments like int (* numb) -> int * numb
|
||||
# negative lookahead prevents matching function pointers (*name)(args) and pointer-to-arrays (*name)[dims]
|
||||
arg_list.gsub!(/\(\s*\*\s*((?:const\s+)?\w+)\s*\)(?!\s*[(\[])/, '* \1')
|
||||
|
||||
# scan argument list for function pointers and replace them with custom types
|
||||
arg_list.gsub!(/([\w\s*]+)\(+([\w\s]*)\*[*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s*]*,?)*)\s*\)*/) do |_m|
|
||||
functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"
|
||||
@@ -581,11 +598,24 @@ class CMockHeaderParser
|
||||
funcname = Regexp.last_match(2).strip
|
||||
funcargs = Regexp.last_match(3).strip
|
||||
funconst = ''
|
||||
funcdecl = ''
|
||||
if funcname.include? 'const'
|
||||
funcname.gsub!('const', '').strip!
|
||||
funconst = 'const '
|
||||
end
|
||||
parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});"
|
||||
# Extract any calling convention from the return type (it belongs in the function pointer declaration)
|
||||
@c_calling_conventions.each do |cc|
|
||||
next unless funcret.include?(cc)
|
||||
|
||||
funcret = funcret.gsub(cc, '').strip
|
||||
funcdecl = cc
|
||||
break
|
||||
end
|
||||
parse_project[:typedefs] << if funcdecl.empty?
|
||||
"typedef #{funcret}(*#{functype})(#{funcargs});"
|
||||
else
|
||||
"typedef #{funcret}(#{funcdecl} *#{functype})(#{funcargs});"
|
||||
end
|
||||
funcname = "cmock_arg#{c += 1}" if funcname.empty?
|
||||
"#{functype} #{funconst}#{funcname}"
|
||||
end
|
||||
@@ -636,6 +666,7 @@ class CMockHeaderParser
|
||||
|
||||
rettype = parsed[:type]
|
||||
rettype = 'void' if @local_as_void.include?(rettype.strip)
|
||||
rettype = 'void' if rettype.empty? && !(@standards + @local_as_void).include?(parsed[:name]) # all return-type tokens were stripped (e.g. bare decorator macros)
|
||||
retstr = parsed[:const_ptr?] ? "#{rettype} const" : rettype
|
||||
decl[:return] = { :type => rettype,
|
||||
:name => 'cmock_to_return',
|
||||
|
||||
+7
-4
@@ -68,7 +68,7 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size)
|
||||
|
||||
/* determine where we're putting this new block, and init its pointer to be the end of the line */
|
||||
index = CMock_Guts_FreePtr + CMOCK_MEM_INDEX_SIZE;
|
||||
*(CMOCK_MEM_INDEX_TYPE*)(&CMock_Guts_Buffer[CMock_Guts_FreePtr]) = CMOCK_GUTS_NONE;
|
||||
CMOCK_MEMSET(&CMock_Guts_Buffer[CMock_Guts_FreePtr], 0, CMOCK_MEM_INDEX_SIZE);
|
||||
CMock_Guts_FreePtr += size;
|
||||
|
||||
return index;
|
||||
@@ -108,7 +108,7 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_
|
||||
next = root;
|
||||
do
|
||||
{
|
||||
index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)next - CMOCK_MEM_INDEX_SIZE);
|
||||
CMOCK_MEMCPY(&index, (unsigned char*)next - CMOCK_MEM_INDEX_SIZE, sizeof(index));
|
||||
if (index >= CMock_Guts_FreePtr)
|
||||
{
|
||||
return CMOCK_GUTS_NONE;
|
||||
@@ -119,7 +119,10 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_
|
||||
}
|
||||
}
|
||||
while (index > 0);
|
||||
*(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)next - CMOCK_MEM_INDEX_SIZE) = (CMOCK_MEM_INDEX_TYPE)((CMOCK_MEM_PTR_AS_INT)obj - (CMOCK_MEM_PTR_AS_INT)CMock_Guts_Buffer);
|
||||
{
|
||||
CMOCK_MEM_INDEX_TYPE tmp = (CMOCK_MEM_INDEX_TYPE)((unsigned char*)obj - CMock_Guts_Buffer);
|
||||
CMOCK_MEMCPY((unsigned char*)next - CMOCK_MEM_INDEX_SIZE, &tmp, sizeof(tmp));
|
||||
}
|
||||
return root_index;
|
||||
}
|
||||
}
|
||||
@@ -141,7 +144,7 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index
|
||||
|
||||
/* if the pointer is good, then use it to look up the next index
|
||||
* (we know the first element always goes in zero, so NEXT must always be > 1) */
|
||||
index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)previous_item - CMOCK_MEM_INDEX_SIZE);
|
||||
CMOCK_MEMCPY(&index, (unsigned char*)previous_item - CMOCK_MEM_INDEX_SIZE, sizeof(index));
|
||||
if ((index > 1) && (index < CMock_Guts_FreePtr))
|
||||
{
|
||||
return index;
|
||||
|
||||
@@ -83,6 +83,18 @@ extern const char* CMockStringMismatch;
|
||||
#define CMOCK_MEM_SIZE (32768)
|
||||
#endif
|
||||
|
||||
/* memory copy/set functions used by CMock internals and generated mocks.
|
||||
* Override to use custom implementations on targets without standard libc. */
|
||||
#if !defined(CMOCK_MEMCPY) || !defined(CMOCK_MEMSET)
|
||||
#include <string.h>
|
||||
#ifndef CMOCK_MEMCPY
|
||||
#define CMOCK_MEMCPY(a, b, c) memcpy(a, b, c)
|
||||
#endif
|
||||
#ifndef CMOCK_MEMSET
|
||||
#define CMOCK_MEMSET(a, b, c) memset(a, b, c)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* automatically calculated defs for easier reading */
|
||||
#define CMOCK_MEM_ALIGN_SIZE (CMOCK_MEM_INDEX_TYPE)(1u << CMOCK_MEM_ALIGN)
|
||||
#define CMOCK_MEM_ALIGN_MASK (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_ALIGN_SIZE - 1)
|
||||
|
||||
@@ -15,3 +15,6 @@
|
||||
:treat_as_void:
|
||||
- OSEK_TASK
|
||||
- VOID_TYPE_CRAZINESS
|
||||
:strippables:
|
||||
- SAMPLE_EXTERN
|
||||
- SAMPLE_MODE
|
||||
|
||||
@@ -18,6 +18,16 @@ typedef struct _POINT_T
|
||||
int y;
|
||||
} POINT_T;
|
||||
|
||||
/* The comments in the following enum are important, as are the newlines and commas */
|
||||
/* This combination caused a curious bug when used together. Make sure it doesn't come back. */
|
||||
typedef enum
|
||||
{
|
||||
MY_ERROR_ID = -18,/**< Driver not ready */
|
||||
MY_OTHER_ID = -19 /**< Node-id is in LSS unconfigured
|
||||
state. If objects are handled properly,
|
||||
his may not be an error. */
|
||||
} MY_STATE_ID_T;
|
||||
|
||||
/* typedef edge case;
|
||||
not ANSI C but it has been done and will break cmock if not handled */
|
||||
typedef void VOID_TYPE_CRAZINESS;
|
||||
@@ -101,3 +111,19 @@ inline int stuff(int num)
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* it seems like CMock didn't love a func-looking struct before a func */
|
||||
#define FOO_TYPE(a) foo_##a
|
||||
struct FOO_TYPE(bar) { int baz; };
|
||||
char b(void);
|
||||
|
||||
/* Here are more macros that sorta look like functions. Only sample_func is real */
|
||||
#define SAMPLE_EXTERN
|
||||
#define SAMPLE_MODE
|
||||
#define SAMPLE_DEPRECATED(a,b)
|
||||
struct struct_a;
|
||||
struct struct_b;
|
||||
SAMPLE_EXTERN SAMPLE_MODE SAMPLE_DEPRECATED(1.1.2, "It was bad. real bad()")
|
||||
void sample_func(struct struct_a **a,
|
||||
struct struct_b **b,
|
||||
...);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# =========================================================================
|
||||
# CMock - Automatic Mock Generation for C
|
||||
# ThrowTheSwitch.org
|
||||
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
||||
# SPDX-License-Identifier: MIT
|
||||
# =========================================================================
|
||||
|
||||
---
|
||||
:cmock:
|
||||
:plugins:
|
||||
- # none
|
||||
|
||||
:systest:
|
||||
:types: |
|
||||
typedef enum {
|
||||
MyTypeA,
|
||||
MyTypeB,
|
||||
MyTypeC,
|
||||
} MyType_t;
|
||||
|
||||
:mockable: |
|
||||
int myFunc(const MyType_t t_MyType);
|
||||
|
||||
:source:
|
||||
:header: |
|
||||
int exercise(const MyType_t t_MyType);
|
||||
|
||||
:code: |
|
||||
int exercise(const MyType_t t_MyType)
|
||||
{
|
||||
return myFunc(t_MyType);
|
||||
}
|
||||
|
||||
:tests:
|
||||
:common: |
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
:units:
|
||||
- :pass: TRUE
|
||||
:should: 'compile and pass when the correct enum value is passed and return value matches'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
myFunc_ExpectAndReturn(MyTypeB, 42);
|
||||
TEST_ASSERT_EQUAL(42, exercise(MyTypeB));
|
||||
}
|
||||
|
||||
- :pass: FALSE
|
||||
:should: 'fail when the wrong enum value is passed'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
myFunc_ExpectAndReturn(MyTypeB, 42);
|
||||
exercise(MyTypeC);
|
||||
}
|
||||
|
||||
- :pass: TRUE
|
||||
:should: 'pass when called with a const MyType_t variable'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
const MyType_t val = MyTypeA;
|
||||
myFunc_ExpectAndReturn(MyTypeA, 0);
|
||||
TEST_ASSERT_EQUAL(0, exercise(val));
|
||||
}
|
||||
|
||||
...
|
||||
@@ -0,0 +1,80 @@
|
||||
# =========================================================================
|
||||
# CMock - Automatic Mock Generation for C
|
||||
# ThrowTheSwitch.org
|
||||
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
||||
# SPDX-License-Identifier: MIT
|
||||
# =========================================================================
|
||||
|
||||
---
|
||||
:cmock:
|
||||
:plugins:
|
||||
- # none
|
||||
|
||||
:systest:
|
||||
:types: |
|
||||
|
||||
:mockable: |
|
||||
int *const foo(float const *const self);
|
||||
|
||||
:source:
|
||||
:header: |
|
||||
int *const exercise(float const *const self);
|
||||
|
||||
:code: |
|
||||
int *const exercise(float const *const self)
|
||||
{
|
||||
return foo(self);
|
||||
}
|
||||
|
||||
:tests:
|
||||
:common: |
|
||||
static float f1 = 1.0f;
|
||||
static float f2 = 2.0f;
|
||||
static int i1 = 10;
|
||||
static int i2 = 20;
|
||||
static float const *const self_a = &f1;
|
||||
static float const *const self_b = &f2;
|
||||
static int *const ret_a = &i1;
|
||||
static int *const ret_b = &i2;
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
:units:
|
||||
- :pass: TRUE
|
||||
:should: 'compile and pass when the correct argument is passed and return value matches'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
foo_ExpectAndReturn(self_a, ret_a);
|
||||
TEST_ASSERT_EQUAL_PTR(ret_a, exercise(self_a));
|
||||
}
|
||||
|
||||
- :pass: FALSE
|
||||
:should: 'fail when the wrong argument pointer is passed'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
foo_ExpectAndReturn(self_a, ret_a);
|
||||
exercise(self_b);
|
||||
}
|
||||
|
||||
- :pass: TRUE
|
||||
:should: 'return the exact pointer provided to ExpectAndReturn'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
foo_ExpectAndReturn(self_a, ret_b);
|
||||
TEST_ASSERT_EQUAL_PTR(ret_b, exercise(self_a));
|
||||
}
|
||||
|
||||
- :pass: FALSE
|
||||
:should: 'fail when the returned pointer does not match what the test expects'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
foo_ExpectAndReturn(self_a, ret_b);
|
||||
TEST_ASSERT_EQUAL_PTR(ret_a, exercise(self_a));
|
||||
}
|
||||
|
||||
...
|
||||
@@ -0,0 +1,88 @@
|
||||
# =========================================================================
|
||||
# CMock - Automatic Mock Generation for C
|
||||
# ThrowTheSwitch.org
|
||||
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
||||
# SPDX-License-Identifier: MIT
|
||||
# =========================================================================
|
||||
|
||||
---
|
||||
#The purpose of this test is to verify that structs containing function pointer
|
||||
#members are not mistakenly mocked by CMock. Only actual function prototypes
|
||||
#at file scope should be mocked. This is especially tricky when the struct
|
||||
#contains nested anonymous structs or unions, because the struct removal regex
|
||||
#can be confused by the inner closing brace and leave function pointer members
|
||||
#in the source for the parser to encounter.
|
||||
:cmock:
|
||||
:plugins:
|
||||
- # none
|
||||
:includes:
|
||||
- "<stdint.h>"
|
||||
|
||||
:systest:
|
||||
:types: |
|
||||
#include <stdint.h>
|
||||
|
||||
/* Forward-declare the type so the source header can use SaladBowl* before
|
||||
the full definition (which lives in the mockable header) is visible. */
|
||||
typedef struct SaladBowlStruct SaladBowl;
|
||||
|
||||
:mockable: |
|
||||
#include <stdint.h>
|
||||
|
||||
/* Full struct definition with a nested anonymous struct and function pointer
|
||||
members. CMock must ignore all of these and only mock saladBowlInit. */
|
||||
struct SaladBowlStruct {
|
||||
struct {
|
||||
uint16_t remainingCapacity;
|
||||
uint16_t ingredientCount;
|
||||
} stats;
|
||||
|
||||
void* (*toss)(struct SaladBowlStruct *self, uint16_t itemSize);
|
||||
int32_t (*empty)(struct SaladBowlStruct *self);
|
||||
void* (*grab)(struct SaladBowlStruct *self, uint16_t itemIndex);
|
||||
void* (*add)(struct SaladBowlStruct *self, uint16_t itemIndex, uint16_t itemSize);
|
||||
int32_t (*pluck)(struct SaladBowlStruct *self, uint16_t itemIndex);
|
||||
};
|
||||
|
||||
int32_t saladBowlInit(SaladBowl *bowl, uint16_t sizeInBytes, uint16_t headerSizeInBytes);
|
||||
void saladBowlBase(void);
|
||||
void saladBowlTop(void);
|
||||
|
||||
/* inlines containing function calls look like function prototypes too */
|
||||
inline void inlineSaladKit(void)
|
||||
{
|
||||
{
|
||||
saladBowlBase();
|
||||
}
|
||||
{
|
||||
saladBowlTop();
|
||||
}
|
||||
}
|
||||
|
||||
:source:
|
||||
:header: |
|
||||
#include <stdint.h>
|
||||
void exercise_salad_bowl(SaladBowl *bowl);
|
||||
|
||||
:code: |
|
||||
void exercise_salad_bowl(SaladBowl *bowl)
|
||||
{
|
||||
saladBowlInit(bowl, 256, 16);
|
||||
}
|
||||
|
||||
:tests:
|
||||
:common: |
|
||||
SaladBowl g_bowl;
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
:units:
|
||||
- :pass: TRUE
|
||||
:should: 'mock only saladBowlInit and ignore function pointer members of the struct'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
saladBowlInit_ExpectAndReturn(&g_bowl, 256, 16, 0);
|
||||
exercise_salad_bowl(&g_bowl);
|
||||
}
|
||||
|
||||
...
|
||||
@@ -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 <string.h>
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
# =========================================================================
|
||||
# CMock - Automatic Mock Generation for C
|
||||
# ThrowTheSwitch.org
|
||||
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
||||
# SPDX-License-Identifier: MIT
|
||||
# =========================================================================
|
||||
|
||||
# Test for issue #67: mock segfault on incorrect expectations in tearDown.
|
||||
# Verifies that calling a mock function in tearDown without a matching
|
||||
# expectation produces a clean failure message rather than a crash.
|
||||
|
||||
---
|
||||
:cmock:
|
||||
:plugins:
|
||||
- # none
|
||||
|
||||
:systest:
|
||||
:types: |
|
||||
|
||||
:mockable: |
|
||||
void init(void);
|
||||
void deinit(void);
|
||||
|
||||
:source:
|
||||
:header: |
|
||||
/* no source functions needed for this test */
|
||||
|
||||
:tests:
|
||||
:common: |
|
||||
void setUp(void) {}
|
||||
void tearDown(void)
|
||||
{
|
||||
/* Simulate issue #67: accidentally call a mock with no expectation.
|
||||
This should produce "Called more times than expected", not a crash. */
|
||||
deinit();
|
||||
}
|
||||
|
||||
:units:
|
||||
- :pass: FALSE
|
||||
:should: 'fail gracefully (not segfault) when a mock is called in tearDown without any expectation'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
/* Empty test body; tearDown calls deinit() with no expectation */
|
||||
}
|
||||
|
||||
- :pass: FALSE
|
||||
:should: 'fail gracefully when mock is called in tearDown even when test body passed'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
/* Test body passes fine; the tearDown call is what causes failure */
|
||||
init_Expect();
|
||||
init();
|
||||
}
|
||||
|
||||
...
|
||||
@@ -0,0 +1,59 @@
|
||||
# =========================================================================
|
||||
# CMock - Automatic Mock Generation for C
|
||||
# ThrowTheSwitch.org
|
||||
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
||||
# SPDX-License-Identifier: MIT
|
||||
# =========================================================================
|
||||
|
||||
# Test for issue #67: mock segfault on incorrect expectations in tearDown.
|
||||
# Verifies that setting mock expectations in tearDown for functions that are
|
||||
# never called produces a clean failure message rather than a crash.
|
||||
|
||||
---
|
||||
:cmock:
|
||||
:plugins:
|
||||
- # none
|
||||
|
||||
:systest:
|
||||
:types: |
|
||||
|
||||
:mockable: |
|
||||
void init(void);
|
||||
void deinit(void);
|
||||
|
||||
:source:
|
||||
:header: |
|
||||
/* no source functions needed for this test */
|
||||
|
||||
:tests:
|
||||
:common: |
|
||||
void setUp(void) {}
|
||||
void tearDown(void)
|
||||
{
|
||||
/* Simulate issue #67: user meant to call deinit_Expect() but accidentally
|
||||
called init_Expect() instead. The init function is never actually called,
|
||||
so CMock should report "Called too few times", not segfault. */
|
||||
init_Expect();
|
||||
}
|
||||
|
||||
:units:
|
||||
- :pass: FALSE
|
||||
:should: 'fail gracefully (not segfault) when wrong expectations are set in tearDown'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
/* Empty test body. tearDown sets up init_Expect() but init is never
|
||||
called, so CMock_Verify should report an unmet expectation. */
|
||||
}
|
||||
|
||||
- :pass: FALSE
|
||||
:should: 'fail gracefully when wrong expectations are set in tearDown even if test body passed'
|
||||
:code: |
|
||||
test()
|
||||
{
|
||||
/* Test body is correct; tearDown's wrong expectation causes the failure */
|
||||
deinit_Expect();
|
||||
deinit();
|
||||
}
|
||||
|
||||
...
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -187,4 +187,118 @@ describe CMockGeneratorPluginExpect, "Verify CMockGeneratorPluginExpect Module W
|
||||
returned = @cmock_generator_plugin_expect.mock_verify(function)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "preserve const-pointer ordering in typedef struct fields for arguments" do
|
||||
function = {
|
||||
:name => "Willow",
|
||||
:args => [
|
||||
{ :name => "ptr_to_const", :type => "const int*", :ptr? => true, :const? => true, :const_ptr? => false },
|
||||
{ :name => "const_ptr", :type => "int*", :ptr? => true, :const? => false, :const_ptr? => true },
|
||||
{ :name => "both_const", :type => "const int*", :ptr? => true, :const? => true, :const_ptr? => true },
|
||||
{ :name => "plain_ptr", :type => "int*", :ptr? => true, :const? => false, :const_ptr? => false },
|
||||
],
|
||||
:return => test_return[:void]
|
||||
}
|
||||
# Struct fields use arg[:type] directly (no reconstruction via arg_type_with_const):
|
||||
# - "const int*" preserved as "const int*" (pointer to const data)
|
||||
# - "int*" (from int* const) stored as "int*" — the const_ptr? is intentionally omitted
|
||||
# because a const struct field can never be written to, making the mock unworkable
|
||||
# - "const int*" (from const int* const) similarly stored without the trailing const
|
||||
expected = " const int* Expected_ptr_to_const;\n" +
|
||||
" int* Expected_const_ptr;\n" +
|
||||
" const int* Expected_both_const;\n" +
|
||||
" int* Expected_plain_ptr;\n"
|
||||
returned = @cmock_generator_plugin_expect.instance_typedefs(function)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "preserve const-before-pointer in return typedef struct field" do
|
||||
const_int_ptr_return = { :type => "const int*", :name => "cmock_to_return", :ptr? => true,
|
||||
:const? => true, :const_ptr? => false, :void? => false,
|
||||
:str => "const int* cmock_to_return" }
|
||||
function = { :name => "Elm", :args => [], :return => const_int_ptr_return }
|
||||
# ReturnVal uses return[:type] = "const int*"
|
||||
expected = " const int* ReturnVal;\n"
|
||||
returned = @cmock_generator_plugin_expect.instance_typedefs(function)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "preserve const on non-pointer custom type in mock function declaration but drop it from struct field" do
|
||||
function = {
|
||||
:name => "myFunc",
|
||||
:args => [{ :name => "t_MyType", :type => "MyType_t", :ptr? => false, :const? => true, :const_ptr? => false }],
|
||||
:args_string => "const MyType_t t_MyType",
|
||||
:args_call => "t_MyType",
|
||||
:return => test_return[:int]
|
||||
}
|
||||
# struct field uses arg[:type] directly — no const, so the field stays writable
|
||||
expected_typedef = " int ReturnVal;\n" \
|
||||
" MyType_t Expected_t_MyType;\n"
|
||||
assert_equal(expected_typedef, @cmock_generator_plugin_expect.instance_typedefs(function))
|
||||
|
||||
# function declaration uses args_string — const MyType_t must appear in the C signature
|
||||
expected_decl = "#define myFunc_Expect(t_MyType) TEST_FAIL_MESSAGE(\"myFunc requires _ExpectAndReturn\");\n" \
|
||||
"#define myFunc_ExpectAndReturn(t_MyType, cmock_retval) myFunc_CMockExpectAndReturn(__LINE__, t_MyType, cmock_retval)\n" \
|
||||
"void myFunc_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, const MyType_t t_MyType, int cmock_to_return);\n"
|
||||
assert_equal(expected_decl, @cmock_generator_plugin_expect.mock_function_declarations(function))
|
||||
end
|
||||
|
||||
it "store const-pointer return value without trailing const in typedef struct field (for writability)" do
|
||||
int_ptr_const_return = { :type => "int*", :name => "cmock_to_return", :ptr? => true,
|
||||
:const? => false, :const_ptr? => true, :void? => false,
|
||||
:str => "int* const cmock_to_return" }
|
||||
function = { :name => "Elm", :args => [], :return => int_ptr_const_return }
|
||||
# ReturnVal uses return[:type] = "int*"; the trailing const is intentionally dropped
|
||||
# so that the struct field remains assignable
|
||||
expected = " int* ReturnVal;\n"
|
||||
returned = @cmock_generator_plugin_expect.instance_typedefs(function)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "preserve const and pointer order in mock function declaration" do
|
||||
int_ptr_const_return = { :type => "int*", :name => "cmock_to_return", :ptr? => true,
|
||||
:const? => false, :const_ptr? => true, :void? => false,
|
||||
:str => "int* const cmock_to_return" }
|
||||
function = {
|
||||
:name => "Cedar",
|
||||
:args => [
|
||||
{ :name => "p", :type => "const int*", :ptr? => true, :const? => true, :const_ptr? => false },
|
||||
{ :name => "q", :type => "int*", :ptr? => true, :const? => false, :const_ptr? => true }
|
||||
],
|
||||
:args_string => "const int* p, int* const q",
|
||||
:args_call => "p, q",
|
||||
:return => int_ptr_const_return
|
||||
}
|
||||
expected = "#define Cedar_Expect(p, q) TEST_FAIL_MESSAGE(\"Cedar requires _ExpectAndReturn\");\n" +
|
||||
"#define Cedar_ExpectAndReturn(p, q, cmock_retval) Cedar_CMockExpectAndReturn(__LINE__, p, q, cmock_retval)\n" +
|
||||
"void Cedar_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, const int* p, int* const q, int* const cmock_to_return);\n"
|
||||
returned = @cmock_generator_plugin_expect.mock_function_declarations(function)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "preserve const-before-pointer return type in mock function declaration for void-arg functions" do
|
||||
const_int_ptr_return = { :type => "const int*", :name => "cmock_to_return", :ptr? => true,
|
||||
:const? => true, :const_ptr? => false, :void? => false,
|
||||
:str => "const int* cmock_to_return" }
|
||||
function = { :name => "Oak", :args => [], :args_string => "void", :args_call => "",
|
||||
:return => const_int_ptr_return }
|
||||
expected = "#define Oak_Expect() TEST_FAIL_MESSAGE(\"Oak requires _ExpectAndReturn\");\n" +
|
||||
"#define Oak_ExpectAndReturn(cmock_retval) Oak_CMockExpectAndReturn(__LINE__, cmock_retval)\n" +
|
||||
"void Oak_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, const int* cmock_to_return);\n"
|
||||
returned = @cmock_generator_plugin_expect.mock_function_declarations(function)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "preserve const-after-pointer return type in mock function declaration for void-arg functions" do
|
||||
int_ptr_const_return = { :type => "int*", :name => "cmock_to_return", :ptr? => true,
|
||||
:const? => false, :const_ptr? => true, :void? => false,
|
||||
:str => "int* const cmock_to_return" }
|
||||
function = { :name => "Oak", :args => [], :args_string => "void", :args_call => "",
|
||||
:return => int_ptr_const_return }
|
||||
expected = "#define Oak_Expect() TEST_FAIL_MESSAGE(\"Oak requires _ExpectAndReturn\");\n" +
|
||||
"#define Oak_ExpectAndReturn(cmock_retval) Oak_CMockExpectAndReturn(__LINE__, cmock_retval)\n" +
|
||||
"void Oak_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, int* const cmock_to_return);\n"
|
||||
returned = @cmock_generator_plugin_expect.mock_function_declarations(function)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -192,13 +193,13 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh
|
||||
" if (cmock_call_instance->ReturnThruPtr_tofu_Used)\n" +
|
||||
" {\n" +
|
||||
" UNITY_TEST_ASSERT_NOT_NULL(tofu, cmock_line, CMockStringPtrIsNULL);\n" +
|
||||
" memcpy((void*)tofu, (const void*)cmock_call_instance->ReturnThruPtr_tofu_Val,\n" +
|
||||
" CMOCK_MEMCPY((void*)tofu, (const void*)cmock_call_instance->ReturnThruPtr_tofu_Val,\n" +
|
||||
" cmock_call_instance->ReturnThruPtr_tofu_Size);\n" +
|
||||
" }\n" +
|
||||
" if (cmock_call_instance->ReturnThruPtr_bean_buffer_Used)\n" +
|
||||
" {\n" +
|
||||
" UNITY_TEST_ASSERT_NOT_NULL(bean_buffer, cmock_line, CMockStringPtrIsNULL);\n" +
|
||||
" memcpy((void*)bean_buffer, (const void*)cmock_call_instance->ReturnThruPtr_bean_buffer_Val,\n" +
|
||||
" CMOCK_MEMCPY((void*)bean_buffer, (const void*)cmock_call_instance->ReturnThruPtr_bean_buffer_Val,\n" +
|
||||
" cmock_call_instance->ReturnThruPtr_bean_buffer_Size);\n" +
|
||||
" }\n"
|
||||
|
||||
@@ -206,4 +207,74 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "converts single pointer type to pointer-to-const via ptr_to_const" do
|
||||
plugin = @cmock_generator_plugin_return_thru_ptr
|
||||
assert_equal("int const*", plugin.ptr_to_const("int*"))
|
||||
assert_equal("char const*", plugin.ptr_to_const("char*"))
|
||||
assert_equal("uint8_t const*", plugin.ptr_to_const("uint8_t*"))
|
||||
assert_equal("void const*", plugin.ptr_to_const("void*"))
|
||||
assert_equal("MY_TYPE const*", plugin.ptr_to_const("MY_TYPE*"))
|
||||
end
|
||||
|
||||
it "converts double pointer type by making inner pointer const via ptr_to_const" do
|
||||
plugin = @cmock_generator_plugin_return_thru_ptr
|
||||
assert_equal("char* const*", plugin.ptr_to_const("char**"))
|
||||
assert_equal("int* const*", plugin.ptr_to_const("int**"))
|
||||
end
|
||||
|
||||
it "includes int* const args (const pointer, mutable data) in typedef but excludes const int* args" do
|
||||
# int* const: const_ptr?=true, const?=false → data is mutable, pointer is const
|
||||
# The condition `!(arg[:const?])` checks whether the POINTED-TO data is const.
|
||||
# const? is about the data, not the pointer itself, so int* const IS included.
|
||||
const_ptr_func = {
|
||||
:name => "Birch",
|
||||
:args => [
|
||||
{ :type => "int*", :name => "mutable_ptr", :ptr? => true, :const? => false, :const_ptr? => false },
|
||||
{ :type => "int*", :name => "const_ptr", :ptr? => true, :const? => false, :const_ptr? => true },
|
||||
{ :type => "const int*", :name => "ptr_to_const", :ptr? => true, :const? => true, :const_ptr? => false },
|
||||
],
|
||||
:return => test_return[:void]
|
||||
}
|
||||
|
||||
@utils.expect :ptr_or_str?, true, ["int*"]
|
||||
@utils.expect :ptr_or_str?, true, ["int*"]
|
||||
@utils.expect :ptr_or_str?, true, ["const int*"]
|
||||
|
||||
# mutable_ptr and const_ptr are included; ptr_to_const is excluded (const?=true)
|
||||
expected = " char ReturnThruPtr_mutable_ptr_Used;\n" +
|
||||
" int const* ReturnThruPtr_mutable_ptr_Val;\n" +
|
||||
" size_t ReturnThruPtr_mutable_ptr_Size;\n" +
|
||||
" char ReturnThruPtr_const_ptr_Used;\n" +
|
||||
" int const* ReturnThruPtr_const_ptr_Val;\n" +
|
||||
" size_t ReturnThruPtr_const_ptr_Size;\n"
|
||||
|
||||
returned = @cmock_generator_plugin_return_thru_ptr.instance_typedefs(const_ptr_func)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "generates correct function signature for int* const args in mock interface" do
|
||||
const_ptr_func = {
|
||||
:name => "Birch",
|
||||
:args => [
|
||||
{ :type => "int*", :name => "const_ptr", :ptr? => true, :const? => false, :const_ptr? => true },
|
||||
],
|
||||
:return => test_return[:void]
|
||||
}
|
||||
|
||||
@utils.expect :ptr_or_str?, true, ["int*"]
|
||||
|
||||
# ptr_to_const("int*") = "int const*", so the helper function takes int const* const_ptr
|
||||
expected =
|
||||
"#define Birch_ReturnThruPtr_const_ptr(const_ptr)" +
|
||||
" Birch_CMockReturnMemThruPtr_const_ptr(__LINE__, const_ptr, sizeof(int))\n" +
|
||||
"#define Birch_ReturnArrayThruPtr_const_ptr(const_ptr, cmock_len)" +
|
||||
" Birch_CMockReturnMemThruPtr_const_ptr(__LINE__, const_ptr, (cmock_len * sizeof(*const_ptr)))\n" +
|
||||
"#define Birch_ReturnMemThruPtr_const_ptr(const_ptr, cmock_size)" +
|
||||
" Birch_CMockReturnMemThruPtr_const_ptr(__LINE__, const_ptr, (cmock_size))\n" +
|
||||
"void Birch_CMockReturnMemThruPtr_const_ptr(UNITY_LINE_TYPE cmock_line, int const* const_ptr, size_t cmock_size);\n"
|
||||
|
||||
returned = @cmock_generator_plugin_return_thru_ptr.mock_function_declarations(const_ptr_func)
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -508,4 +508,65 @@ describe CMockGeneratorUtils, "Verify CMockGeneratorUtils Module" do
|
||||
" }\n"
|
||||
assert_equal(expected, utils.code_verify_an_arg_expectation(function, arg))
|
||||
end
|
||||
|
||||
it 'correctly reconstruct type strings preserving const and pointer order' do
|
||||
# non-pointer: no const
|
||||
assert_equal("int", CMockGeneratorUtils.arg_type_with_const({:type => "int", :ptr? => false, :const? => false, :const_ptr? => false}))
|
||||
# non-pointer: const (prepended)
|
||||
assert_equal("const int", CMockGeneratorUtils.arg_type_with_const({:type => "int", :ptr? => false, :const? => true, :const_ptr? => false}))
|
||||
|
||||
# pointer to mutable: no const
|
||||
assert_equal("int*", CMockGeneratorUtils.arg_type_with_const({:type => "int*", :ptr? => true, :const? => false, :const_ptr? => false}))
|
||||
|
||||
# pointer to const (const int*): const already in :type, no trailing const
|
||||
assert_equal("const int*", CMockGeneratorUtils.arg_type_with_const({:type => "const int*", :ptr? => true, :const? => true, :const_ptr? => false}))
|
||||
|
||||
# const pointer (int* const): :type has no const, const_ptr? appends " const"
|
||||
assert_equal("int* const", CMockGeneratorUtils.arg_type_with_const({:type => "int*", :ptr? => true, :const? => false, :const_ptr? => true}))
|
||||
|
||||
# const pointer to const (const int* const)
|
||||
assert_equal("const int* const", CMockGeneratorUtils.arg_type_with_const({:type => "const int*", :ptr? => true, :const? => true, :const_ptr? => true}))
|
||||
|
||||
# trailing-const form: int const* (same semantics as const int* but different spelling)
|
||||
assert_equal("int const*", CMockGeneratorUtils.arg_type_with_const({:type => "int const*", :ptr? => true, :const? => true, :const_ptr? => false}))
|
||||
|
||||
# trailing-const pointer to const: int const* const
|
||||
assert_equal("int const* const", CMockGeneratorUtils.arg_type_with_const({:type => "int const*", :ptr? => true, :const? => true, :const_ptr? => true}))
|
||||
|
||||
# custom type pointer
|
||||
assert_equal("MY_TYPE*", CMockGeneratorUtils.arg_type_with_const({:type => "MY_TYPE*", :ptr? => true, :const? => false, :const_ptr? => false}))
|
||||
assert_equal("const MY_TYPE", CMockGeneratorUtils.arg_type_with_const({:type => "MY_TYPE", :ptr? => false, :const? => true, :const_ptr? => false}))
|
||||
|
||||
# double pointer: no const_ptr, so :type used as-is
|
||||
assert_equal("int**", CMockGeneratorUtils.arg_type_with_const({:type => "int**", :ptr? => true, :const? => false, :const_ptr? => false}))
|
||||
assert_equal("const int**", CMockGeneratorUtils.arg_type_with_const({:type => "const int**", :ptr? => true, :const? => true, :const_ptr? => false}))
|
||||
# double pointer with const_ptr: appends " const" after last *
|
||||
assert_equal("int** const", CMockGeneratorUtils.arg_type_with_const({:type => "int**", :ptr? => true, :const? => false, :const_ptr? => true}))
|
||||
end
|
||||
|
||||
it 'produce correct C declarations preserving const and pointer order' do
|
||||
# const pointer to mutable int: int* const p
|
||||
arg = {:type => "int*", :name => "p", :ptr? => true, :const? => false, :const_ptr? => true}
|
||||
assert_equal("int* const p", CMockGeneratorUtils.arg_declaration(arg))
|
||||
|
||||
# pointer to const int: const int* p
|
||||
arg = {:type => "const int*", :name => "p", :ptr? => true, :const? => true, :const_ptr? => false}
|
||||
assert_equal("const int* p", CMockGeneratorUtils.arg_declaration(arg))
|
||||
|
||||
# const pointer to const int: const int* const p
|
||||
arg = {:type => "const int*", :name => "p", :ptr? => true, :const? => true, :const_ptr? => true}
|
||||
assert_equal("const int* const p", CMockGeneratorUtils.arg_declaration(arg))
|
||||
|
||||
# trailing-const form: int const* const p
|
||||
arg = {:type => "int const*", :name => "p", :ptr? => true, :const? => true, :const_ptr? => true}
|
||||
assert_equal("int const* const p", CMockGeneratorUtils.arg_declaration(arg))
|
||||
|
||||
# plain pointer: int* p
|
||||
arg = {:type => "int*", :name => "p", :ptr? => true, :const? => false, :const_ptr? => false}
|
||||
assert_equal("int* p", CMockGeneratorUtils.arg_declaration(arg))
|
||||
|
||||
# non-pointer const: const MY_TYPE v
|
||||
arg = {:type => "MY_TYPE", :name => "v", :ptr? => false, :const? => true, :const_ptr? => false}
|
||||
assert_equal("const MY_TYPE v", CMockGeneratorUtils.arg_declaration(arg))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1515,6 +1515,33 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
|
||||
assert_equal(typedefs, result[:typedefs])
|
||||
end
|
||||
|
||||
it "extract functions using a function pointer with shorthand notation and a calling convention" do
|
||||
source = "void FunkyTurkey(void __stdcall * func_ptr(int arg0))"
|
||||
expected = [{ :var_arg=>nil,
|
||||
:return=>{ :type => "void",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => false,
|
||||
:const? => false,
|
||||
:const_ptr? => false,
|
||||
:str => "void cmock_to_return",
|
||||
:void? => true
|
||||
},
|
||||
:name=>"FunkyTurkey",
|
||||
:unscoped_name=>"FunkyTurkey",
|
||||
:namespace=>[],
|
||||
:class=>nil,
|
||||
:modifier=>"",
|
||||
:contains_ptr? => false,
|
||||
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :string? => false, :const? => false, :const_ptr? => false}
|
||||
],
|
||||
:args_string=>"cmock_module_func_ptr1 func_ptr",
|
||||
:args_call=>"func_ptr" }]
|
||||
typedefs = ["typedef void *(__stdcall *cmock_module_func_ptr1)(int arg0);"]
|
||||
result = @parser.parse("module", source)
|
||||
assert_equal(expected, result[:functions])
|
||||
assert_equal(typedefs, result[:typedefs])
|
||||
end
|
||||
|
||||
it "extract functions containing a function pointer with a void" do
|
||||
source = "void FunkyTurkey(void (*func_ptr)(void))"
|
||||
expected = [{ :var_arg=>nil,
|
||||
@@ -1845,6 +1872,59 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
|
||||
assert_equal(typedefs, result[:typedefs])
|
||||
end
|
||||
|
||||
it "extract functions containing a parenthesized pointer argument" do
|
||||
source = "void func(char *str, int (* numb))"
|
||||
expected = [{ :var_arg=>nil,
|
||||
:return=>{ :type => "void",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => false,
|
||||
:const? => false,
|
||||
:const_ptr? => false,
|
||||
:str => "void cmock_to_return",
|
||||
:void? => true
|
||||
},
|
||||
:name=>"func",
|
||||
:unscoped_name=>"func",
|
||||
:namespace=>[],
|
||||
:class=>nil,
|
||||
:modifier=>"",
|
||||
:contains_ptr? => true,
|
||||
:args=>[ {:type=>"char*", :name=>"str", :ptr? => false, :string? => true, :const? => false, :const_ptr? => false},
|
||||
{:type=>"int*", :name=>"numb", :ptr? => true, :string? => false, :const? => false, :const_ptr? => false}
|
||||
],
|
||||
:args_string=>"char* str, int* numb",
|
||||
:args_call=>"str, numb" }]
|
||||
result = @parser.parse("module", source)
|
||||
assert_equal(expected, result[:functions])
|
||||
assert_equal([], result[:typedefs])
|
||||
end
|
||||
|
||||
it "extract functions containing a parenthesized const pointer argument" do
|
||||
source = "void func(int (* const numb))"
|
||||
expected = [{ :var_arg=>nil,
|
||||
:return=>{ :type => "void",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => false,
|
||||
:const? => false,
|
||||
:const_ptr? => false,
|
||||
:str => "void cmock_to_return",
|
||||
:void? => true
|
||||
},
|
||||
:name=>"func",
|
||||
:unscoped_name=>"func",
|
||||
:namespace=>[],
|
||||
:class=>nil,
|
||||
:modifier=>"",
|
||||
:contains_ptr? => true,
|
||||
:args=>[ {:type=>"int*", :name=>"numb", :ptr? => true, :string? => false, :const? => false, :const_ptr? => true}
|
||||
],
|
||||
:args_string=>"int* const numb",
|
||||
:args_call=>"numb" }]
|
||||
result = @parser.parse("module", source)
|
||||
assert_equal(expected, result[:functions])
|
||||
assert_equal([], result[:typedefs])
|
||||
end
|
||||
|
||||
it "extract functions with varargs" do
|
||||
source = "int XFiles(int Scully, int Mulder, ...);\n"
|
||||
expected = [{ :var_arg=>"...",
|
||||
@@ -3003,4 +3083,169 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
|
||||
assert_equal(true, src_len_arg[:array_size?], "src_len should be marked as array_size?")
|
||||
end
|
||||
|
||||
it "correctly parse const int* const return type (both const qualifiers)" do
|
||||
sources = [
|
||||
"const int* const DoubleConst(void);\n",
|
||||
"const int *const DoubleConst(void);\n",
|
||||
]
|
||||
|
||||
# The trailing 'const' (const_ptr?) also lands in :modifier, matching the existing
|
||||
# behavior seen for 'int* const' return types (see the test above for PorkRoast).
|
||||
# The generated mock correctly uses return[:str] and function_return_type() which
|
||||
# reconstruct the full "const int* const" from :type + const_ptr? without needing :modifier.
|
||||
expected = [{ :var_arg => nil,
|
||||
:name => "DoubleConst",
|
||||
:unscoped_name => "DoubleConst",
|
||||
:namespace => [],
|
||||
:class => nil,
|
||||
:return => { :type => "const int*",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => true,
|
||||
:const? => true,
|
||||
:const_ptr? => true,
|
||||
:str => "const int* const cmock_to_return",
|
||||
:void? => false
|
||||
},
|
||||
:modifier => "const",
|
||||
:contains_ptr? => false,
|
||||
:args => [],
|
||||
:args_string => "void",
|
||||
:args_call => ""
|
||||
}]
|
||||
|
||||
sources.each do |source|
|
||||
assert_equal(expected, @parser.parse("module", source)[:functions])
|
||||
end
|
||||
end
|
||||
|
||||
it "correctly parse int const* const return type (trailing-const form, both qualifiers)" do
|
||||
sources = [
|
||||
"int const* const DoubleConst(void);\n",
|
||||
"int const *const DoubleConst(void);\n",
|
||||
]
|
||||
|
||||
expected = [{ :var_arg => nil,
|
||||
:name => "DoubleConst",
|
||||
:unscoped_name => "DoubleConst",
|
||||
:namespace => [],
|
||||
:class => nil,
|
||||
:return => { :type => "int const*",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => true,
|
||||
:const? => true,
|
||||
:const_ptr? => true,
|
||||
:str => "int const* const cmock_to_return",
|
||||
:void? => false
|
||||
},
|
||||
:modifier => "const",
|
||||
:contains_ptr? => false,
|
||||
:args => [],
|
||||
:args_string => "void",
|
||||
:args_call => ""
|
||||
}]
|
||||
|
||||
sources.each do |source|
|
||||
assert_equal(expected, @parser.parse("module", source)[:functions])
|
||||
end
|
||||
end
|
||||
|
||||
it "correctly parse double-pointer argument types preserving const and pointer ordering" do
|
||||
# Tests the full parse pipeline for double-pointer args (not just divine_ptr_and_const).
|
||||
# const int** p → const? false (const is not before the final *), type preserves "const int**"
|
||||
# int** const q → const_ptr? true, type is "int**"
|
||||
# int* const* p → const? true (const* before the last *), type is "int* const*"
|
||||
source = "void TriplePlay(const int** a, int** const b, int* const* c);\n"
|
||||
|
||||
expected = [{ :var_arg => nil,
|
||||
:name => "TriplePlay",
|
||||
:unscoped_name => "TriplePlay",
|
||||
:namespace => [],
|
||||
:class => nil,
|
||||
:return => { :type => "void",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => false,
|
||||
:const? => false,
|
||||
:const_ptr? => false,
|
||||
:str => "void cmock_to_return",
|
||||
:void? => true
|
||||
},
|
||||
:modifier => "",
|
||||
:contains_ptr? => true,
|
||||
:args => [
|
||||
{ :type => "const int**", :name => "a", :ptr? => true, :string? => false,
|
||||
:const? => false, :const_ptr? => false },
|
||||
{ :type => "int**", :name => "b", :ptr? => true, :string? => false,
|
||||
:const? => false, :const_ptr? => true },
|
||||
{ :type => "int* const*", :name => "c", :ptr? => true, :string? => false,
|
||||
:const? => true, :const_ptr? => false },
|
||||
],
|
||||
:args_string => "const int** a, int** const b, int* const* c",
|
||||
:args_call => "a, b, c"
|
||||
}]
|
||||
|
||||
assert_equal(expected, @parser.parse("module", source)[:functions])
|
||||
end
|
||||
|
||||
it "correctly parse double-pointer argument types with all consts" do
|
||||
# const int** const p → both const forms: type "const int**", const?=false, const_ptr?=true
|
||||
# int* const* const q → both const forms: type "int* const*", const?=true, const_ptr?=true
|
||||
source = "void AllConst(const int** const a, int* const* const b);\n"
|
||||
|
||||
expected = [{ :var_arg => nil,
|
||||
:name => "AllConst",
|
||||
:unscoped_name => "AllConst",
|
||||
:namespace => [],
|
||||
:class => nil,
|
||||
:return => { :type => "void",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => false,
|
||||
:const? => false,
|
||||
:const_ptr? => false,
|
||||
:str => "void cmock_to_return",
|
||||
:void? => true
|
||||
},
|
||||
:modifier => "",
|
||||
:contains_ptr? => true,
|
||||
:args => [
|
||||
{ :type => "const int**", :name => "a", :ptr? => true, :string? => false,
|
||||
:const? => false, :const_ptr? => true },
|
||||
{ :type => "int* const*", :name => "b", :ptr? => true, :string? => false,
|
||||
:const? => true, :const_ptr? => true },
|
||||
],
|
||||
:args_string => "const int** const a, int* const* const b",
|
||||
:args_call => "a, b"
|
||||
}]
|
||||
|
||||
assert_equal(expected, @parser.parse("module", source)[:functions])
|
||||
end
|
||||
|
||||
it "preserve const on non-pointer custom type arguments (e.g. const MyType_t)" do
|
||||
source = "int myFunc(const MyType_t t_MyType);\n"
|
||||
|
||||
expected = [{ :var_arg => nil,
|
||||
:name => "myFunc",
|
||||
:unscoped_name => "myFunc",
|
||||
:namespace => [],
|
||||
:class => nil,
|
||||
:return => { :type => "int",
|
||||
:name => 'cmock_to_return',
|
||||
:ptr? => false,
|
||||
:const? => false,
|
||||
:const_ptr? => false,
|
||||
:str => "int cmock_to_return",
|
||||
:void? => false
|
||||
},
|
||||
:modifier => "",
|
||||
:contains_ptr? => false,
|
||||
:args => [
|
||||
{ :type => "MyType_t", :name => "t_MyType", :ptr? => false, :string? => false,
|
||||
:const? => true, :const_ptr? => false }
|
||||
],
|
||||
:args_string => "const MyType_t t_MyType",
|
||||
:args_call => "t_MyType"
|
||||
}]
|
||||
|
||||
assert_equal(expected, @parser.parse("module", source)[:functions])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user