Merge pull request #534 from ThrowTheSwitch/bugfix/interactions

Bugfix/interactions
This commit is contained in:
Mark VanderVoord
2026-06-26 15:36:59 -04:00
committed by GitHub
17 changed files with 1093 additions and 8 deletions
+23
View File
@@ -881,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
==========
+1
View File
@@ -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"
+39 -1
View File
@@ -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
+20 -1
View File
@@ -251,6 +251,8 @@ 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
@@ -565,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}"
@@ -592,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
+7 -4
View File
@@ -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;
+12
View File
@@ -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)
@@ -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();
}
...
+4
View File
@@ -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
+61
View File
@@ -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
+245
View File
@@ -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