mirror of
https://github.com/meekrosoft/fff
synced 2026-01-23 00:15:59 +01:00
Merge pull request #113 from yperess/peress/custom_fake_sig
Support custom function signatures
This commit is contained in:
26
README.md
26
README.md
@@ -459,9 +459,17 @@ The basic mechanism that fff provides you in this case is the custom_fake field
|
||||
|
||||
You need to create a custom function (e.g. getTime_custom_fake) to produce the output optionally by use of a helper variable (e.g. getTime_custom_now) to retrieve that output from. Then some creativity to tie it all together. The most important part (IMHO) is to keep your test case readable and maintainable.
|
||||
|
||||
In case your project uses a C compiler that supports nested functions (e.g. GCC) you can even combine all this in a single unit test function so you can easily oversee all details of the test.
|
||||
In case your project uses a C compiler that supports nested functions (e.g. GCC), or when using C++ lambdas, you can even combine all this in a single unit test function so you can easily oversee all details of the test.
|
||||
|
||||
```c
|
||||
#include <functional>
|
||||
|
||||
/* Configure FFF to use std::function, which enables capturing lambdas */
|
||||
#define CUSTOM_FFF_FUNCTION_TEMPLATE(RETURN, FUNCNAME, ...) \
|
||||
std::function<RETURN (__VA_ARGS__)> FUNCNAME
|
||||
|
||||
#include "fff.h"
|
||||
|
||||
/* The time structure */
|
||||
typedef struct {
|
||||
int hour, min;
|
||||
@@ -474,15 +482,13 @@ FAKE_VOID_FUNC(getTime, Time*);
|
||||
TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_output)
|
||||
{
|
||||
Time t;
|
||||
Time getTime_custom_now;
|
||||
void getTime_custom_fake(Time *now) {
|
||||
*now = getTime_custom_now;
|
||||
}
|
||||
getTime_fake.custom_fake = getTime_custom_fake;
|
||||
|
||||
/* given a specific time */
|
||||
getTime_custom_now.hour = 13;
|
||||
getTime_custom_now.min = 05;
|
||||
Time getTime_custom_now = {
|
||||
.hour = 13,
|
||||
.min = 05,
|
||||
};
|
||||
getTime_fake.custom_fake = [getTime_custom_now](Time *now) {
|
||||
*now = getTime_custom_now;
|
||||
};
|
||||
|
||||
/* when getTime is called */
|
||||
getTime(&t);
|
||||
|
||||
40
fakegen.rb
40
fakegen.rb
@@ -35,10 +35,21 @@ def output_constants
|
||||
putd "#define FFF_GCC_FUNCTION_ATTRIBUTES"
|
||||
}
|
||||
putd "#endif"
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
def output_default_function_pointer_macro(has_calling_conventions)
|
||||
name = has_calling_conventions ? "(CALLING_CONVENTION *FUNCNAME)" : "(*FUNCNAME)"
|
||||
calling_conv = has_calling_conventions ? ", CALLING_CONVENTION" : ""
|
||||
putd "#ifndef CUSTOM_FFF_FUNCTION_TEMPLATE"
|
||||
putd_backslash "#define CUSTOM_FFF_FUNCTION_TEMPLATE(RETURN#{calling_conv}, FUNCNAME, ...)"
|
||||
indent {
|
||||
putd "RETURN#{name}(__VA_ARGS__)"
|
||||
}
|
||||
putd "#endif /* CUSTOM_FFF_FUNCTION_TEMPLATE */"
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -235,7 +246,9 @@ def define_reset_fake_helper
|
||||
indent {
|
||||
putd_backslash "void FUNCNAME##_reset(void){"
|
||||
indent {
|
||||
putd_backslash "memset(&FUNCNAME##_fake, 0, sizeof(FUNCNAME##_fake));"
|
||||
putd_backslash "memset((void*)&FUNCNAME##_fake, 0, sizeof(FUNCNAME##_fake) - sizeof(FUNCNAME##_fake.custom_fake) - sizeof(FUNCNAME##_fake.custom_fake_seq));"
|
||||
putd_backslash "FUNCNAME##_fake.custom_fake = NULL;"
|
||||
putd_backslash "FUNCNAME##_fake.custom_fake_seq = NULL;"
|
||||
putd_backslash "FUNCNAME##_fake.arg_history_len = FFF_ARG_HISTORY_LEN;"
|
||||
}
|
||||
putd "}"
|
||||
@@ -359,6 +372,14 @@ def arg_val_list(args_count)
|
||||
arguments.join(", ")
|
||||
end
|
||||
|
||||
#example: ARG0_TYPE, ARG1_TYPE
|
||||
def arg_type_list(args_count)
|
||||
return "void" if (args_count == 0)
|
||||
arguments = []
|
||||
args_count.times { |i| arguments << "ARG#{i}_TYPE" }
|
||||
arguments.join(", ")
|
||||
end
|
||||
|
||||
#example: arg0, arg1
|
||||
def arg_list(args_count)
|
||||
arguments = []
|
||||
@@ -374,17 +395,15 @@ end
|
||||
def output_custom_function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
||||
ap_list = has_varargs ? ", va_list ap" : ""
|
||||
signature = has_calling_conventions ? "(CALLING_CONVENTION *custom_fake)" : "(*custom_fake)"
|
||||
signature += "(#{arg_val_list(arg_count)}#{ap_list});"
|
||||
putd_backslash return_type + signature
|
||||
calling_conv = has_calling_conventions ? ", CALLING_CONVENTION" : ""
|
||||
putd_backslash "CUSTOM_FFF_FUNCTION_TEMPLATE(#{return_type}#{calling_conv}, custom_fake, #{arg_type_list(arg_count)}#{ap_list});"
|
||||
end
|
||||
|
||||
def output_custom_function_array(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
||||
ap_list = has_varargs ? ", va_list ap" : ""
|
||||
custom_array = has_calling_conventions ? "(CALLING_CONVENTION **custom_fake_seq)" : "(**custom_fake_seq)"
|
||||
custom_array += "(#{arg_val_list(arg_count)}#{ap_list});"
|
||||
putd_backslash return_type + custom_array
|
||||
calling_conv = has_calling_conventions ? ", CALLING_CONVENTION" : ""
|
||||
putd_backslash "CUSTOM_FFF_FUNCTION_TEMPLATE(#{return_type}#{calling_conv}, *custom_fake_seq, #{arg_type_list(arg_count)}#{ap_list});"
|
||||
end
|
||||
|
||||
# example: RETURN_TYPE FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
||||
@@ -483,7 +502,7 @@ def output_function_body(arg_count, has_varargs, is_value_function)
|
||||
putd_backslash "}"
|
||||
}
|
||||
putd_backslash "}"
|
||||
putd_backslash "if (FUNCNAME##_fake.custom_fake){ "
|
||||
putd_backslash "if (FUNCNAME##_fake.custom_fake != NULL){ "
|
||||
indent {
|
||||
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});" unless not is_value_function
|
||||
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
||||
@@ -644,6 +663,7 @@ def output_c_and_cpp(has_calling_conventions)
|
||||
include_guard {
|
||||
include_dependencies
|
||||
output_constants
|
||||
output_default_function_pointer_macro(has_calling_conventions)
|
||||
output_internal_helper_macros
|
||||
yield
|
||||
output_macro_counting_shortcuts(has_calling_conventions)
|
||||
@@ -675,4 +695,4 @@ help {
|
||||
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, has_calling_conventions, false)}
|
||||
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, has_calling_conventions, true)}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,36 +4,56 @@
|
||||
# Create a list of common files needed for tests
|
||||
set(
|
||||
COMMON_FILE_LIST
|
||||
c_test_framework.h
|
||||
test_cases.include
|
||||
include/c_test_framework.h
|
||||
src/test_cases.include
|
||||
)
|
||||
|
||||
# Create the C test executable
|
||||
add_executable(c_test fff_test_c.c ${COMMON_FILE_LIST})
|
||||
add_executable(c_test src/fff_test_c.c ${COMMON_FILE_LIST})
|
||||
target_include_directories(c_test PRIVATE include)
|
||||
target_link_libraries(c_test PRIVATE fff)
|
||||
|
||||
# Create the C++ test executable
|
||||
add_executable(cpp_test fff_test_cpp.cpp ${COMMON_FILE_LIST})
|
||||
add_executable(cpp_test src/fff_test_cpp.cpp ${COMMON_FILE_LIST})
|
||||
target_include_directories(cpp_test PRIVATE include)
|
||||
target_link_libraries(cpp_test PRIVATE gtest fff)
|
||||
|
||||
# Create the C global test executable
|
||||
add_executable(c_global_test
|
||||
fff_test_global_c.c
|
||||
global_fakes.c
|
||||
global_fakes.h
|
||||
src/fff_test_global_c.c
|
||||
src/global_fakes.c
|
||||
include/global_fakes.h
|
||||
${COMMON_FILE_LIST}
|
||||
)
|
||||
target_include_directories(c_global_test PRIVATE include)
|
||||
target_link_libraries(c_global_test PRIVATE fff)
|
||||
|
||||
# Create the C++ global test executable
|
||||
add_executable(cpp_global_test
|
||||
fff_test_global_cpp.cpp
|
||||
global_fakes.c
|
||||
global_fakes.h
|
||||
src/fff_test_global_cpp.cpp
|
||||
src/global_fakes.c
|
||||
include/global_fakes.h
|
||||
${COMMON_FILE_LIST}
|
||||
)
|
||||
target_include_directories(cpp_global_test PRIVATE include)
|
||||
target_link_libraries(cpp_global_test PRIVATE gtest fff)
|
||||
|
||||
# Create the C++ custom function signature executable
|
||||
add_executable(cpp_custom_fn_signature_test
|
||||
src/fff_test_custom_function_template.cpp
|
||||
${COMMON_FILE_LIST}
|
||||
)
|
||||
target_include_directories(cpp_custom_fn_signature_test PRIVATE include)
|
||||
target_link_libraries(cpp_custom_fn_signature_test PRIVATE gtest fff)
|
||||
# Due to a bug in WinLibs for Windows it's not currently possible to use:
|
||||
# target_precompile_headers(cpp_custom_fn_signature_test PUBLIC include/custom_function.hpp)
|
||||
# See more info at target_precompile_headers(cpp_custom_fn_signature_test PUBLIC include/custom_function.hpp)
|
||||
|
||||
add_test(
|
||||
NAME cpp_custom_fn_signature_test
|
||||
COMMAND $<TARGET_FILE:cpp_custom_fn_signature_test>
|
||||
)
|
||||
|
||||
# Add the tests for ctest
|
||||
add_test(
|
||||
NAME c_test
|
||||
|
||||
10
test/include/custom_function.hpp
Normal file
10
test/include/custom_function.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
/* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define CUSTOM_FFF_FUNCTION_TEMPLATE(RETURN, FUNCNAME, ...) \
|
||||
std::function<RETURN (__VA_ARGS__)> FUNCNAME
|
||||
11
test/include/fff_wrapper.hpp
Normal file
11
test/include/fff_wrapper.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
/* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "custom_function.hpp"
|
||||
|
||||
#include <fff.h>
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef GLOBAL_FAKES_H_
|
||||
#define GLOBAL_FAKES_H_
|
||||
|
||||
#include "fff.h"
|
||||
#include "string.h"
|
||||
#include <fff.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//// Imaginary production code header file ///
|
||||
@@ -13,7 +13,7 @@
|
||||
#define FFF_CALL_HISTORY_LEN OVERRIDE_CALL_HIST_LEN
|
||||
|
||||
#include "fff.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
DEFINE_FFF_GLOBALS
|
||||
|
||||
29
test/src/fff_test_custom_function_template.cpp
Normal file
29
test/src/fff_test_custom_function_template.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "fff_wrapper.hpp"
|
||||
|
||||
DEFINE_FFF_GLOBALS
|
||||
|
||||
FAKE_VOID_FUNC(do_stuff, int);
|
||||
|
||||
class FFFCustomFakeSuite : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
RESET_FAKE(do_stuff);
|
||||
FFF_RESET_HISTORY();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(FFFCustomFakeSuite, custom_cpp_fake_function)
|
||||
{
|
||||
int x = 0;
|
||||
do_stuff_fake.custom_fake = [&x](int i) {
|
||||
x = i;
|
||||
};
|
||||
do_stuff(5);
|
||||
ASSERT_EQ(5, x);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
extern "C"{
|
||||
#include "global_fakes.h"
|
||||
}
|
||||
#include <gtest/gtest.h>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
DEFINE_FFF_GLOBALS;
|
||||
|
||||
@@ -244,9 +244,11 @@ void voidfunc1outparam_custom_fake3(char *a)
|
||||
|
||||
TEST_F(FFFTestSuite, custom_fake_sequence_not_exausthed)
|
||||
{
|
||||
void (*custom_fakes[])(char *) = {voidfunc1outparam_custom_fake1,
|
||||
voidfunc1outparam_custom_fake2,
|
||||
voidfunc1outparam_custom_fake3};
|
||||
CUSTOM_FFF_FUNCTION_TEMPLATE(void, custom_fakes[], char *) = {
|
||||
voidfunc1outparam_custom_fake1,
|
||||
voidfunc1outparam_custom_fake2,
|
||||
voidfunc1outparam_custom_fake3
|
||||
};
|
||||
char a = 'a';
|
||||
|
||||
SET_CUSTOM_FAKE_SEQ(voidfunc1outparam, custom_fakes, 3);
|
||||
@@ -304,9 +306,10 @@ long custom_longfunc3(void)
|
||||
|
||||
TEST_F(FFFTestSuite, custom_fake_seq_return_values_saved_in_history)
|
||||
{
|
||||
long (*custom_fakes[])(void) = {custom_longfunc1,
|
||||
custom_longfunc2,
|
||||
custom_longfunc3};
|
||||
CUSTOM_FFF_FUNCTION_TEMPLATE(long, custom_fakes[], void) = {
|
||||
custom_longfunc1,
|
||||
custom_longfunc2,
|
||||
custom_longfunc3};
|
||||
|
||||
SET_CUSTOM_FAKE_SEQ(longfunc0, custom_fakes, 3);
|
||||
|
||||
@@ -321,9 +324,10 @@ TEST_F(FFFTestSuite, custom_fake_seq_return_values_saved_in_history)
|
||||
|
||||
TEST_F(FFFTestSuite, custom_fake_sequence_exhausted)
|
||||
{
|
||||
void (*custom_fakes[])(char *) = {voidfunc1outparam_custom_fake1,
|
||||
voidfunc1outparam_custom_fake2,
|
||||
voidfunc1outparam_custom_fake3};
|
||||
CUSTOM_FFF_FUNCTION_TEMPLATE(void, custom_fakes[], char *) = {
|
||||
voidfunc1outparam_custom_fake1,
|
||||
voidfunc1outparam_custom_fake2,
|
||||
voidfunc1outparam_custom_fake3};
|
||||
char a = 'a';
|
||||
|
||||
SET_CUSTOM_FAKE_SEQ(voidfunc1outparam, custom_fakes, 3);
|
||||
@@ -399,9 +403,10 @@ int valuefunc3var_custom_fake3(const char *str, int a, va_list vl)
|
||||
|
||||
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};
|
||||
CUSTOM_FFF_FUNCTION_TEMPLATE(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);
|
||||
@@ -411,9 +416,10 @@ TEST_F(FFFTestSuite, vararg_custom_fake_sequence_not_exhausted)
|
||||
|
||||
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};
|
||||
CUSTOM_FFF_FUNCTION_TEMPLATE(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);
|
||||
@@ -426,9 +432,10 @@ TEST_F(FFFTestSuite, vararg_custom_fake_seq_return_values_saved_in_history)
|
||||
|
||||
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};
|
||||
CUSTOM_FFF_FUNCTION_TEMPLATE(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);
|
||||
@@ -440,9 +447,10 @@ TEST_F(FFFTestSuite, vararg_custom_fake_sequence_exhausted)
|
||||
|
||||
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};
|
||||
CUSTOM_FFF_FUNCTION_TEMPLATE(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);
|
||||
@@ -534,4 +542,3 @@ TEST_F(FFFTestSuite, value_func_can_capture_upto_20_arguments_correctly)
|
||||
ASSERT_EQ(18, valuefunc20_fake.arg18_val);
|
||||
ASSERT_EQ(19, valuefunc20_fake.arg19_val);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user