1
0
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:
Mike Long
2022-09-08 19:57:02 +02:00
committed by GitHub
15 changed files with 368 additions and 259 deletions

View File

@@ -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);

View File

@@ -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)}
}
}
}

412
fff.h

File diff suppressed because it is too large Load Diff

View File

@@ -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

View 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

View 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>

View File

@@ -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 ///

View 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

View 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);
}

View File

@@ -2,7 +2,7 @@
extern "C"{
#include "global_fakes.h"
}
#include <gtest/gtest.h>
#include "gtest/gtest.h"
DEFINE_FFF_GLOBALS;

View File

@@ -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);
}