mirror of
https://github.com/meekrosoft/fff
synced 2026-01-23 08:25:59 +01:00
Custom return value delegate sequences for variadic functions (#63)
* Custom return value delegate sequences for variadic functions * Added unit tests for variadic functions custom return value delegate sequences * Fixes in code style * Variadic functions custom delegates also tested in C++ * Fixed some compilation warnings * Added test for variadic function custom delegates sequence reset * Updated documentation with variadic functions custom delegate sequences * Update README.md * Minor style changes. Thank you very much @oliviera9!
This commit is contained in:
@@ -432,6 +432,9 @@ could call the real `fprintf()` like this:
|
||||
return vfprintf(stream, format, ap);
|
||||
}
|
||||
|
||||
Just like [return value delegates](#custom-return-value-delegate-sequences), you can also specify sequences for variadic functions using `SET_CUSTOM_FAKE_SEQ`.
|
||||
See the test files for examples.
|
||||
|
||||
## How do I specify calling conventions for my fake functions?
|
||||
|
||||
fff has a limited capability for enabling specification of Microsoft's Visual C/C++ calling conventions, but this support must be enabled when generating fff's header file `fff.h`.
|
||||
|
||||
29
fakegen.rb
29
fakegen.rb
@@ -408,6 +408,35 @@ def output_function_body(arg_count, has_varargs, is_value_function)
|
||||
putd_backslash "REGISTER_CALL(FUNCNAME);"
|
||||
|
||||
if has_varargs
|
||||
return_type = is_value_function ? "return " : ""
|
||||
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_len){ /* a sequence of custom fakes */"
|
||||
indent {
|
||||
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_idx < FUNCNAME##_fake.custom_fake_seq_len){"
|
||||
indent {
|
||||
putd_backslash "va_list ap;"
|
||||
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
||||
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)}, ap);" unless not is_value_function
|
||||
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
||||
putd_backslash "va_end(ap);" unless not is_value_function
|
||||
putd_backslash "return ret;" unless not is_value_function
|
||||
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)}, ap);" unless is_value_function
|
||||
putd_backslash "va_end(ap);" unless is_value_function
|
||||
}
|
||||
putd_backslash "}"
|
||||
putd_backslash "else{"
|
||||
indent {
|
||||
putd_backslash "va_list ap;"
|
||||
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
||||
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)}, ap);" unless not is_value_function
|
||||
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
||||
putd_backslash "va_end(ap);" unless not is_value_function
|
||||
putd_backslash "return ret;" unless not is_value_function
|
||||
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)}, ap);"
|
||||
putd_backslash "va_end(ap);" unless is_value_function
|
||||
}
|
||||
putd_backslash "}"
|
||||
}
|
||||
putd_backslash "}"
|
||||
putd_backslash "if(FUNCNAME##_fake.custom_fake){"
|
||||
indent {
|
||||
putd_backslash "RETURN_TYPE ret;" if is_value_function
|
||||
|
||||
@@ -28,8 +28,8 @@ FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
||||
FAKE_VALUE_FUNC(long, longfunc0);
|
||||
FAKE_VALUE_FUNC(enum MYBOOL, enumfunc0);
|
||||
FAKE_VALUE_FUNC(struct MyStruct, structfunc0);
|
||||
FAKE_VOID_FUNC_VARARG(voidfunc3var, char *, int, ...);
|
||||
FAKE_VALUE_FUNC_VARARG(int, valuefunc3var, char *, int, ...);
|
||||
FAKE_VOID_FUNC_VARARG(voidfunc3var, const char *, int, ...);
|
||||
FAKE_VALUE_FUNC_VARARG(int, valuefunc3var, const char *, int, ...);
|
||||
FAKE_VALUE_FUNC(int, strlcpy3, char* const, const char* const, const size_t);
|
||||
FAKE_VOID_FUNC(voidfunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||
FAKE_VALUE_FUNC(int, valuefunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||
@@ -116,6 +116,10 @@ int main()
|
||||
|
||||
RUN_TEST(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments);
|
||||
RUN_TEST(FFFTestSuite, use_value_vararg_fake_with_different_number_of_arguments);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_sequence_not_exhausted);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_seq_return_values_saved_in_history);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_sequence_exhausted);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_sequence_reset);
|
||||
|
||||
RUN_TEST(FFFTestSuite, can_capture_upto_20_arguments_correctly);
|
||||
RUN_TEST(FFFTestSuite, value_func_can_capture_upto_20_arguments_correctly);
|
||||
|
||||
@@ -22,6 +22,8 @@ FAKE_VOID_FUNC(voidfunc1, int);
|
||||
FAKE_VOID_FUNC(voidfunc2, char, char);
|
||||
FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
||||
FAKE_VALUE_FUNC(long, longfunc0);
|
||||
FAKE_VOID_FUNC_VARARG(voidfunc3var, const char *, int, ...);
|
||||
FAKE_VALUE_FUNC_VARARG(int, valuefunc3var, const char *, int, ...);
|
||||
FAKE_VOID_FUNC(voidfunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||
FAKE_VALUE_FUNC(int, valuefunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||
#else
|
||||
@@ -29,6 +31,8 @@ FAKE_VOID_FUNC(__cdecl, voidfunc1, int);
|
||||
FAKE_VOID_FUNC(__cdecl, voidfunc2, char, char);
|
||||
FAKE_VOID_FUNC(__cdecl, voidfunc1outparam, char *);
|
||||
FAKE_VALUE_FUNC(long, __cdecl, longfunc0);
|
||||
FAKE_VOID_FUNC_VARARG(__cdecl, voidfunc3var, const char *, int, ...);
|
||||
FAKE_VALUE_FUNC_VARARG(int, __cdecl, valuefunc3var, const char *, int, ...);
|
||||
FAKE_VOID_FUNC(__cdecl, voidfunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||
FAKE_VALUE_FUNC(int, __cdecl, valuefunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||
#endif
|
||||
@@ -42,6 +46,8 @@ public:
|
||||
RESET_FAKE(voidfunc2);
|
||||
RESET_FAKE(longfunc0);
|
||||
RESET_FAKE(voidfunc1outparam);
|
||||
RESET_FAKE(voidfunc3var);
|
||||
RESET_FAKE(valuefunc3var);
|
||||
FFF_RESET_HISTORY();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,6 +68,10 @@ int main()
|
||||
|
||||
RUN_TEST(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments);
|
||||
RUN_TEST(FFFTestSuite, use_value_vararg_fake_with_different_number_of_arguments);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_sequence_not_exhausted);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_seq_return_values_saved_in_history);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_sequence_exhausted);
|
||||
RUN_TEST(FFFTestSuite, vararg_custom_fake_sequence_reset);
|
||||
|
||||
RUN_TEST(FFFTestSuite, can_capture_upto_20_arguments_correctly);
|
||||
RUN_TEST(FFFTestSuite, value_func_can_capture_upto_20_arguments_correctly);
|
||||
|
||||
@@ -15,6 +15,8 @@ public:
|
||||
RESET_FAKE(voidfunc2);
|
||||
RESET_FAKE(longfunc0);
|
||||
RESET_FAKE(voidfunc1outparam);
|
||||
RESET_FAKE(voidfunc3var);
|
||||
RESET_FAKE(valuefunc3var);
|
||||
FFF_RESET_HISTORY();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -354,7 +354,88 @@ TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_return
|
||||
ASSERT_EQ(MEANING_OF_LIFE, retval);
|
||||
}
|
||||
|
||||
#ifndef __cplusplus
|
||||
int valuefunc3var_custom_fake1(const char *str, int a, va_list vl)
|
||||
{
|
||||
int arg;
|
||||
while ((arg = va_arg(vl, int)) != 0)
|
||||
a += arg;
|
||||
return a;
|
||||
}
|
||||
|
||||
int valuefunc3var_custom_fake2(const char *str, int a, va_list vl)
|
||||
{
|
||||
int arg;
|
||||
while ((arg = va_arg(vl, int)) != 0)
|
||||
a -= arg;
|
||||
return a;
|
||||
}
|
||||
|
||||
int valuefunc3var_custom_fake3(const char *str, int a, va_list vl)
|
||||
{
|
||||
int arg;
|
||||
while ((arg = va_arg(vl, int)) != 0)
|
||||
a *= arg;
|
||||
return a;
|
||||
}
|
||||
|
||||
TEST_F(FFFTestSuite, vararg_custom_fake_sequence_not_exhausted)
|
||||
{
|
||||
int (*custom_fakes[])(const char *, int, va_list) = {valuefunc3var_custom_fake1,
|
||||
valuefunc3var_custom_fake2,
|
||||
valuefunc3var_custom_fake3};
|
||||
SET_CUSTOM_FAKE_SEQ(valuefunc3var, custom_fakes, 3);
|
||||
int a = 1;
|
||||
ASSERT_EQ(valuefunc3var("a", a, 2, 3, 4, 0), 10);
|
||||
ASSERT_EQ(valuefunc3var("aa", a, 2, 3, 4, 2, 0), -10);
|
||||
ASSERT_EQ(valuefunc3var("aaa", a, 2, 3, 0), 6);
|
||||
}
|
||||
|
||||
TEST_F(FFFTestSuite, vararg_custom_fake_seq_return_values_saved_in_history)
|
||||
{
|
||||
int (*custom_fakes[])(const char *, int, va_list) = {valuefunc3var_custom_fake1,
|
||||
valuefunc3var_custom_fake2,
|
||||
valuefunc3var_custom_fake3};
|
||||
SET_CUSTOM_FAKE_SEQ(valuefunc3var, custom_fakes, 3);
|
||||
int a = 1;
|
||||
valuefunc3var("a", a, 2, 3, 4, 0);
|
||||
valuefunc3var("aa", a, 2, 3, 4, 2, 0);
|
||||
valuefunc3var("aaa", a, 2, 3, 0);
|
||||
ASSERT_EQ(10, valuefunc3var_fake.return_val_history[0]);
|
||||
ASSERT_EQ(-10, valuefunc3var_fake.return_val_history[1]);
|
||||
ASSERT_EQ(6, valuefunc3var_fake.return_val_history[2]);
|
||||
}
|
||||
|
||||
TEST_F(FFFTestSuite, vararg_custom_fake_sequence_exhausted)
|
||||
{
|
||||
int (*custom_fakes[])(const char *, int, va_list) = {valuefunc3var_custom_fake1,
|
||||
valuefunc3var_custom_fake2,
|
||||
valuefunc3var_custom_fake3};
|
||||
SET_CUSTOM_FAKE_SEQ(valuefunc3var, custom_fakes, 3);
|
||||
int a = 1;
|
||||
ASSERT_EQ(valuefunc3var("a", a, 2, 3, 4, 0), 10);
|
||||
ASSERT_EQ(valuefunc3var("aa", a, 2, 3, 4, 2, 0), -10);
|
||||
ASSERT_EQ(valuefunc3var("aaa", a, 2, 3, 0), 6);
|
||||
ASSERT_EQ(valuefunc3var("aaa", a, 4, 5, 1, 0), 20);
|
||||
ASSERT_EQ(valuefunc3var("aaa", a, 4, 0), 4);
|
||||
}
|
||||
|
||||
TEST_F(FFFTestSuite, vararg_custom_fake_sequence_reset)
|
||||
{
|
||||
int (*custom_fakes[])(const char *, int, va_list) = {valuefunc3var_custom_fake1,
|
||||
valuefunc3var_custom_fake2,
|
||||
valuefunc3var_custom_fake3};
|
||||
SET_CUSTOM_FAKE_SEQ(valuefunc3var, custom_fakes, 3);
|
||||
int a = 1;
|
||||
ASSERT_EQ(valuefunc3var("a", a, 2, 3, 4, 0), 10);
|
||||
ASSERT_EQ(valuefunc3var("aa", a, 2, 3, 4, 2, 0), -10);
|
||||
RESET_FAKE(valuefunc3var);
|
||||
ASSERT_EQ(0, valuefunc3var("b", 7, 4, 5));
|
||||
valuefunc3var_fake.return_val = 1;
|
||||
ASSERT_EQ(1, valuefunc3var("c", 5, 4, 4));
|
||||
valuefunc3var_fake.return_val = 2;
|
||||
ASSERT_EQ(2, valuefunc3var("d", 6, 3, 5));
|
||||
}
|
||||
|
||||
TEST_F(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments)
|
||||
{
|
||||
voidfunc3var("0 parameters", 0);
|
||||
@@ -362,7 +443,7 @@ TEST_F(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments)
|
||||
voidfunc3var("2 parameters", 2, 10, 20);
|
||||
voidfunc3var("3 parameters", 3, 10, 20, 30);
|
||||
|
||||
ASSERT_EQ(voidfunc3var_fake.call_count, 4);
|
||||
ASSERT_EQ(voidfunc3var_fake.call_count, 4u);
|
||||
char msg[] = "3 parameters";
|
||||
ASSERT_EQ(strcmp(voidfunc3var_fake.arg0_val, msg), 0);
|
||||
ASSERT_EQ(3, voidfunc3var_fake.arg1_val);
|
||||
@@ -375,12 +456,11 @@ TEST_F(FFFTestSuite, use_value_vararg_fake_with_different_number_of_arguments)
|
||||
valuefunc3var("2 parameters", 2, 10, 20);
|
||||
valuefunc3var("3 parameters", 3, 10, 20, 30);
|
||||
|
||||
ASSERT_EQ(valuefunc3var_fake.call_count, 4);
|
||||
ASSERT_EQ(valuefunc3var_fake.call_count, 4u);
|
||||
char msg[] = "3 parameters";
|
||||
ASSERT_EQ(strcmp(valuefunc3var_fake.arg0_val, msg), 0);
|
||||
ASSERT_EQ(3, valuefunc3var_fake.arg1_val);
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
TEST_F(FFFTestSuite, can_capture_upto_20_arguments_correctly)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user