forked from 3rd-party/fff
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
488442255c | ||
|
|
d4fed915a3 | ||
|
|
25d4c616be | ||
|
|
21f1693002 | ||
|
|
78555cb85c | ||
|
|
2eb067e5a1 | ||
|
|
ff70585de8 | ||
|
|
c325073b44 | ||
|
|
bdb739732b | ||
|
|
c9bebd9d14 | ||
|
|
7e09f07e5b | ||
|
|
b9f11dcd8a | ||
|
|
ab47e7fc5b | ||
|
|
f9c08aec81 | ||
|
|
cf2c4780ef | ||
|
|
995f26b939 | ||
|
|
0b9e9f5064 | ||
|
|
2c5ecf5495 | ||
|
|
f38b06e939 | ||
|
|
96827e3a39 | ||
|
|
fe54d15393 | ||
|
|
b76c7c7168 | ||
|
|
35b26878a7 | ||
|
|
eabb622ea0 | ||
|
|
4e031416d6 | ||
|
|
5b930a1382 | ||
|
|
8e0e312556 | ||
|
|
955db6c60c | ||
|
|
0376823b0c | ||
|
|
5fb4dc64e8 | ||
|
|
6011dfde79 | ||
|
|
d0328c9e4a | ||
|
|
840a619e19 | ||
|
|
11b70375a6 | ||
|
|
54c35b6a1b | ||
|
|
3553583f57 | ||
|
|
dc667cbf82 | ||
|
|
15cb0ecbac | ||
|
|
e0ca6b9e72 | ||
|
|
1cac9afc41 | ||
|
|
45c4d31690 | ||
|
|
1c981604a1 | ||
|
|
d0a214c584 | ||
|
|
a31a9ee38c | ||
|
|
cd727d4195 | ||
|
|
86c5e44614 | ||
|
|
b8f9e65387 |
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Compiler, toolset, platform (please complete the following information):**
|
||||||
|
- OS: [e.g. Ubuntu 16.04 LTS]
|
||||||
|
- Compiler: [e.g. gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Thank you for your contribution.
|
||||||
|
|
||||||
|
Before submitting this PR, please make sure:
|
||||||
|
|
||||||
|
- [ ] Your code builds clean without any errors or warnings
|
||||||
|
- [ ] You are not breaking consistency
|
||||||
|
- [ ] You have added unit tests
|
||||||
|
- [ ] All tests and other checks pass
|
||||||
25
.github/workflows/verify_pr.yaml
vendored
Normal file
25
.github/workflows/verify_pr.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Check PR
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
push:
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-pr:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cmake -GNinja -B build
|
||||||
|
cmake --build build
|
||||||
|
ctest --test-dir build --output-on-failure
|
||||||
|
|
||||||
36
.gitignore
vendored
36
.gitignore
vendored
@@ -2,3 +2,39 @@ build/
|
|||||||
*~
|
*~
|
||||||
*.sublime-project
|
*.sublime-project
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# IntelliJ IDE project directory
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|||||||
34
CMakeLists.txt
Normal file
34
CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
project(fff)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
# Add the gtest library which will be used below
|
||||||
|
add_subdirectory(gtest)
|
||||||
|
|
||||||
|
# Enable ctest
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
# Generate fff.h if fakegen.rb changed
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/fff.h
|
||||||
|
COMMAND
|
||||||
|
ruby ${CMAKE_CURRENT_LIST_DIR}/fakegen.rb >> ${CMAKE_CURRENT_LIST_DIR}/fff.h
|
||||||
|
DEPENDS
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/fakegen.rb
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/LICENSE
|
||||||
|
)
|
||||||
|
add_custom_target(fff_h DEPENDS ${CMAKE_CURRENT_LIST_DIR}/fff.h)
|
||||||
|
|
||||||
|
# Add an interface library for fff.h
|
||||||
|
add_library(fff INTERFACE)
|
||||||
|
add_dependencies(fff fff_h)
|
||||||
|
target_include_directories(fff INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
||||||
|
# Add tests and samples
|
||||||
|
add_subdirectory(test)
|
||||||
|
add_subdirectory(examples)
|
||||||
10
Makefile
10
Makefile
@@ -1,10 +0,0 @@
|
|||||||
all:
|
|
||||||
mkdir -p build
|
|
||||||
cd gtest; $(MAKE) all
|
|
||||||
cd test; $(MAKE) all
|
|
||||||
cd examples; $(MAKE) all
|
|
||||||
|
|
||||||
clean:
|
|
||||||
cd gtest; $(MAKE) clean
|
|
||||||
cd test; $(MAKE) clean
|
|
||||||
cd examples; $(MAKE) clean
|
|
||||||
155
README.md
155
README.md
@@ -2,13 +2,45 @@
|
|||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
[](https://travis-ci.org/meekrosoft/fff)
|
[](https://travis-ci.org/meekrosoft/fff)
|
||||||
|
[](https://ci.appveyor.com/project/wulfgarpro/fff/branch/master)
|
||||||
|
[](https://gitter.im/wulfgarpro/fff?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
|
||||||
|
|
||||||
|
- [A Fake Function Framework for C](#a-fake-function-framework-for-c)
|
||||||
|
- [Hello Fake World!](#hello-fake-world)
|
||||||
|
- [Capturing Arguments](#capturing-arguments)
|
||||||
|
- [Return Values](#return-values)
|
||||||
|
- [Resetting a Fake](#resetting-a-fake)
|
||||||
|
- [Call History](#call-history)
|
||||||
|
- [Default Argument History](#default-argument-history)
|
||||||
|
- [User Defined Argument History](#user-defined-argument-history)
|
||||||
|
- [Function Return Value Sequences](#function-return-value-sequences)
|
||||||
|
- [Custom Return Value Delegate](#custom-return-value-delegate)
|
||||||
|
- [Return Value History](#return-value-history)
|
||||||
|
- [Variadic Functions](#variadic-functions)
|
||||||
|
- [Common Questions](#common-questions)
|
||||||
|
- [Specifying GCC Function Attributes](#specifying-gcc-function-attributes)
|
||||||
|
- [Find Out More](#find-out-more)
|
||||||
|
- [Benefits](#benefits)
|
||||||
|
- [Under the Hood](#under-the-hood)
|
||||||
|
- [Cheat Sheet](#cheat-sheet)
|
||||||
|
|
||||||
## A Fake Function Framework for C
|
## A Fake Function Framework for C
|
||||||
fff is a micro-framework for creating fake C functions for tests. Because life
|
fff is a micro-framework for creating fake C functions for tests. Because life
|
||||||
is too short to spend time hand-writing fake functions for testing.
|
is too short to spend time hand-writing fake functions for testing.
|
||||||
|
|
||||||
|
## Running all tests
|
||||||
|
|
||||||
## Hello fake world!
|
### Linux / MacOS
|
||||||
|
To run all the tests and sample apps, simply call `$ buildandtest`. This script
|
||||||
|
will call down into CMake with the following:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cmake -GNinja -B build
|
||||||
|
cmake --build build
|
||||||
|
ctest --test-dir build --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hello Fake World!
|
||||||
|
|
||||||
Say you are testing an embedded user interface and you have a function that
|
Say you are testing an embedded user interface and you have a function that
|
||||||
you want to create a fake for:
|
you want to create a fake for:
|
||||||
@@ -62,11 +94,7 @@ typedef struct DISPLAY_init_Fake {
|
|||||||
DISPLAY_init_Fake DISPLAY_init_fake;
|
DISPLAY_init_Fake DISPLAY_init_fake;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Capturing Arguments
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Capturing arguments
|
|
||||||
|
|
||||||
Ok, enough with the toy examples. What about faking functions with arguments?
|
Ok, enough with the toy examples. What about faking functions with arguments?
|
||||||
|
|
||||||
@@ -104,9 +132,7 @@ type (a char pointer in this example).
|
|||||||
A variable is created for every argument in the form
|
A variable is created for every argument in the form
|
||||||
`"function_name"fake.argN_val`
|
`"function_name"fake.argN_val`
|
||||||
|
|
||||||
|
## Return Values
|
||||||
|
|
||||||
## Return values
|
|
||||||
|
|
||||||
When you want to define a fake function that returns a value, you should use the
|
When you want to define a fake function that returns a value, you should use the
|
||||||
`FAKE_VALUE_FUNC` macro. For instance:
|
`FAKE_VALUE_FUNC` macro. For instance:
|
||||||
@@ -153,10 +179,7 @@ you would use a syntax like this:
|
|||||||
```c
|
```c
|
||||||
FAKE_VALUE_FUNC(double, pow, double, double);
|
FAKE_VALUE_FUNC(double, pow, double, double);
|
||||||
```
|
```
|
||||||
|
## Resetting a Fake
|
||||||
|
|
||||||
|
|
||||||
## Resetting a fake
|
|
||||||
|
|
||||||
Good tests are isolated tests, so it is important to reset the fakes for each
|
Good tests are isolated tests, so it is important to reset the fakes for each
|
||||||
unit test. All the fakes have a reset function to reset their arguments and
|
unit test. All the fakes have a reset function to reset their arguments and
|
||||||
@@ -196,9 +219,9 @@ void setup()
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Call history
|
## Call History
|
||||||
Say you want to test that a function calls functionA, then functionB, then
|
Say you want to test that a function calls functionA, then functionB, then
|
||||||
functionA again, how would you do that? Well `fff` maintains a call
|
functionA again, how would you do that? Well fff maintains a call
|
||||||
history so that it is easy to assert these expectations.
|
history so that it is easy to assert these expectations.
|
||||||
|
|
||||||
Here's how it works:
|
Here's how it works:
|
||||||
@@ -260,7 +283,7 @@ The other is to check if the call count is greater than the history size:
|
|||||||
ASSERT(voidfunc2_fake.arg_history_len < voidfunc2_fake.call_count);
|
ASSERT(voidfunc2_fake.arg_history_len < voidfunc2_fake.call_count);
|
||||||
```
|
```
|
||||||
|
|
||||||
The argument histories for a fake function are reset when the RESET_FAKE
|
The argument histories for a fake function are reset when the `RESET_FAKE`
|
||||||
function is called
|
function is called
|
||||||
|
|
||||||
## User Defined Argument History
|
## User Defined Argument History
|
||||||
@@ -277,7 +300,6 @@ override the default by defining it before include the `fff.h` like this:
|
|||||||
#include "../fff.h"
|
#include "../fff.h"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Function Return Value Sequences
|
## Function Return Value Sequences
|
||||||
|
|
||||||
Often in testing we would like to test the behaviour of sequence of function call
|
Often in testing we would like to test the behaviour of sequence of function call
|
||||||
@@ -308,7 +330,7 @@ value in the sequence indefinitely.
|
|||||||
## Custom Return Value Delegate
|
## Custom Return Value Delegate
|
||||||
|
|
||||||
You can specify your own function to provide the return value for the fake. This
|
You can specify your own function to provide the return value for the fake. This
|
||||||
is done by setting the custom_fake member of the fake. Here's an example:
|
is done by setting the `custom_fake` member of the fake. Here's an example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define MEANING_OF_LIFE 42
|
#define MEANING_OF_LIFE 42
|
||||||
@@ -324,13 +346,13 @@ TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_return
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Custom Return Value Delegate Sequences
|
### Custom Return Value Delegate Sequences
|
||||||
|
|
||||||
Say you have a function with an out parameter, and you want it to have a different behaviour
|
Say you have a function with an out parameter, and you want it to have a different behaviour
|
||||||
on the first three calls, for example: set the value 'x' to the out parameter on the first call,
|
on the first three calls, for example: set the value 'x' to the out parameter on the first call,
|
||||||
the value 'y' to the out parameter on the second call, and the value 'z' to the out parameter
|
the value 'y' to the out parameter on the second call, and the value 'z' to the out parameter
|
||||||
on the third call. You can specify a sequence of custom functions to a non-variadic function
|
on the third call. You can specify a sequence of custom functions to a non-variadic function
|
||||||
using the SET_CUSTOM_FAKE_SEQ macro. Here's an example:
|
using the `SET_CUSTOM_FAKE_SEQ` macro. Here's an example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void voidfunc1outparam_custom_fake1(char *a)
|
void voidfunc1outparam_custom_fake1(char *a)
|
||||||
@@ -366,14 +388,15 @@ TEST_F(FFFTestSuite, custom_fake_sequence_not_exausthed)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The fake will call your custom functions in the order specified by the SET_CUSTOM_FAKE_SEQ
|
The fake will call your custom functions in the order specified by the `SET_CUSTOM_FAKE_SEQ`
|
||||||
macro. When the last custom fake is reached the fake will keep calling the last custom
|
macro. When the last custom fake is reached the fake will keep calling the last custom
|
||||||
fake in the sequence. This macro works much like the SET_RETURN_SEQ macro.
|
fake in the sequence. This macro works much like the `SET_RETURN_SEQ` macro.
|
||||||
|
|
||||||
|
## Return Value History
|
||||||
|
|
||||||
## Return value history
|
|
||||||
Say you have two functions f1 and f2. f2 must be called to release some resource
|
Say you have two functions f1 and f2. f2 must be called to release some resource
|
||||||
allocated by f1, but only in the cases where f1 returns zero. f1 could be
|
allocated by f1, but only in the cases where f1 returns zero. f1 could be
|
||||||
pthread_mutex_trylock and f2 could be pthread_mutex_unlock. <tt>fff</tt> will
|
pthread_mutex_trylock and f2 could be pthread_mutex_unlock. fff will
|
||||||
save the history of returned values so this can be easily checked, even when
|
save the history of returned values so this can be easily checked, even when
|
||||||
you use a sequence of custom fakes. Here's a simple example:
|
you use a sequence of custom fakes. Here's a simple example:
|
||||||
|
|
||||||
@@ -389,18 +412,18 @@ you use a sequence of custom fakes. Here's a simple example:
|
|||||||
ASSERT_EQ(myReturnVals[2], longfunc0_fake.return_val_history[2]);
|
ASSERT_EQ(myReturnVals[2], longfunc0_fake.return_val_history[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
You access the returned values in the <tt>return_val_history</tt> field.
|
You access the returned values in the `return_val_history` field.
|
||||||
|
|
||||||
## Variadic Functions
|
## Variadic Functions
|
||||||
|
|
||||||
You can fake variadic functions using the macros <tt>FAKE_VALUE_FUNC_VARARG</tt>
|
You can fake variadic functions using the macros `FAKE_VALUE_FUNC_VARARG`
|
||||||
and <tt>FAKE_VOID_FUNC_VARARG</tt>. For instance:
|
and `FAKE_VOID_FUNC_VARARG`. For instance:
|
||||||
|
|
||||||
FAKE_VALUE_FUNC_VARARG(int, fprintf, FILE *, const char*, ...);
|
FAKE_VALUE_FUNC_VARARG(int, fprintf, FILE *, const char*, ...);
|
||||||
|
|
||||||
In order to access the variadic parameters from a custom fake function, declare a
|
In order to access the variadic parameters from a custom fake function, declare a
|
||||||
<tt>va_list</tt> parameter. For instance, a custom fake for <tt>fprintf()</tt>
|
`va_list` parameter. For instance, a custom fake for `fprintf()`
|
||||||
could call the real <tt>fprintf()</tt> like this:
|
could call the real `fprintf()` like this:
|
||||||
|
|
||||||
int fprintf_custom(FILE *stream, const char *format, va_list ap) {
|
int fprintf_custom(FILE *stream, const char *format, va_list ap) {
|
||||||
if (fprintf0_fake.return_val < 0) // should we fail?
|
if (fprintf0_fake.return_val < 0) // should we fail?
|
||||||
@@ -408,8 +431,31 @@ could call the real <tt>fprintf()</tt> like this:
|
|||||||
return vfprintf(stream, format, ap);
|
return vfprintf(stream, format, ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
## How do I fake a function that returns a value by reference?
|
Just like [return value delegates](#custom-return-value-delegate-sequences), you can also specify sequences for variadic functions using `SET_CUSTOM_FAKE_SEQ`.
|
||||||
The basic mechanism that FFF provides you in this case is the custom_fake field described in the *Custom Return Value Delegate* example above.
|
See the test files for examples.
|
||||||
|
|
||||||
|
## Common Questions
|
||||||
|
|
||||||
|
### 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`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ruby fakegen.rb --with-calling-conventions > fff.h
|
||||||
|
```
|
||||||
|
|
||||||
|
By enabling this support, all of fff's fake function scaffolding will necessitate the specification of a calling convention, e.g. `__cdecl` for each VALUE or VOID fake.
|
||||||
|
|
||||||
|
Here are some basic examples: take note that the placement of the calling convention being specified is different depending on whether the fake is a VOID or VALUE function.
|
||||||
|
|
||||||
|
```c
|
||||||
|
FAKE_VOID_FUNC(__cdecl, voidfunc1, int);
|
||||||
|
FAKE_VALUE_FUNC(long, __cdecl, longfunc0);
|
||||||
|
```
|
||||||
|
|
||||||
|
### How do I fake a function that returns a value by reference?
|
||||||
|
|
||||||
|
The basic mechanism that fff provides you in this case is the custom_fake field described in the *Custom Return Value Delegate* example above.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@@ -447,8 +493,9 @@ TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_output
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## How do I fake a function with a function pointer parameter?
|
### How do I fake a function with a function pointer parameter?
|
||||||
Using FFF to stub functions that have function pointer parameter can cause problems when trying to stub them. Presented here is an example how to deal with this situation.
|
|
||||||
|
Using fff to stub functions that have function pointer parameter can cause problems when trying to stub them. Presented here is an example how to deal with this situation.
|
||||||
|
|
||||||
If you need to stub a function that has a function pointer parameter, e.g. something like:
|
If you need to stub a function that has a function pointer parameter, e.g. something like:
|
||||||
|
|
||||||
@@ -458,7 +505,7 @@ typedef int timer_handle;
|
|||||||
extern int timer_start(timer_handle handle, long delay, void (*cb_function) (int arg), int arg);
|
extern int timer_start(timer_handle handle, long delay, void (*cb_function) (int arg), int arg);
|
||||||
```
|
```
|
||||||
|
|
||||||
Then creating a fake like below will horribly fail when trying to compile because the FFF macro will internally expand into an illegal variable ```int (*)(int) arg2_val```.
|
Then creating a fake like below will horribly fail when trying to compile because the fff macro will internally expand into an illegal variable ```int (*)(int) arg2_val```.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
/* The fake, attempt one */
|
/* The fake, attempt one */
|
||||||
@@ -473,7 +520,7 @@ FAKE_VALUE_FUNC(int,
|
|||||||
The solution to this problem is to create a bridging type that needs only to be visible in the unit tester. The fake will use that intermediate type. This way the compiler will not complain because the types match.
|
The solution to this problem is to create a bridging type that needs only to be visible in the unit tester. The fake will use that intermediate type. This way the compiler will not complain because the types match.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
/* Additional type needed to be able to use callback in FFF */
|
/* Additional type needed to be able to use callback in fff */
|
||||||
typedef void (*timer_cb) (int argument);
|
typedef void (*timer_cb) (int argument);
|
||||||
|
|
||||||
/* The fake, attempt two */
|
/* The fake, attempt two */
|
||||||
@@ -530,8 +577,10 @@ TEST_F(FFFTestSuite, test_fake_with_function_pointer)
|
|||||||
ASSERT_EQ(cb_timeout_called, 1);
|
ASSERT_EQ(cb_timeout_called, 1);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
## How do I reuse a fake across multiple test-suites?
|
|
||||||
FFF functions like FAKE_VALUE_FUNC will perform both the declaration AND the definition of the fake function and the corresponding data structs. This cannot be placed in a header, since it will lead to multiple definitions of the fake functions.
|
### How do I reuse a fake across multiple test-suites?
|
||||||
|
|
||||||
|
fff functions like `FAKE_VALUE_FUNC` will perform both the declaration AND the definition of the fake function and the corresponding data structs. This cannot be placed in a header, since it will lead to multiple definitions of the fake functions.
|
||||||
|
|
||||||
The solution is to separate declaration and definition of the fakes, and place the declaration into a public header file, and the definition into a private source file.
|
The solution is to separate declaration and definition of the fakes, and place the declaration into a public header file, and the definition into a private source file.
|
||||||
|
|
||||||
@@ -557,7 +606,28 @@ DEFINE_FAKE_VOID_FUNC_VARARG(void_function_vargs, const char *, int, ...);
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Find out more...
|
## Specifying GCC Function Attributes
|
||||||
|
|
||||||
|
You can specify GCC function attributes for your fakes using the `FFF_GCC_FUNCTION_ATTRIBUTES` directive.
|
||||||
|
|
||||||
|
### Weak Functions
|
||||||
|
|
||||||
|
One usful attribute is the _weak_ attribute that marks a function such that it can be overridden by a non-weak variant at link time. Using weak functions in combination with fff can help simplify your testing approach.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
* Define a library of fake functions, e.g. libfake.a.
|
||||||
|
* Link a binary (you might have many) that defines a subset of real variants of the fake functions to the aforementioned fake library.
|
||||||
|
* This has the benefit of allowing a binary to selectively use a subset of the required fake functions while testing the real variants without the need for many different make targets.
|
||||||
|
|
||||||
|
You can mark all fakes with the weak attribute like so:
|
||||||
|
```
|
||||||
|
#define FFF_GCC_FUNCTION_ATTRIBUTES __attribute__((weak))
|
||||||
|
#include "fff.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example project that demonstrates the above approach: _./examples/weak_linking_.
|
||||||
|
|
||||||
|
## Find Out More
|
||||||
|
|
||||||
Look under the examples directory for full length examples in both C and C++.
|
Look under the examples directory for full length examples in both C and C++.
|
||||||
There is also a test suite for the framework under the test directory.
|
There is also a test suite for the framework under the test directory.
|
||||||
@@ -572,10 +642,11 @@ So whats the point?
|
|||||||
* To work in both C and C++ test environments
|
* To work in both C and C++ test environments
|
||||||
|
|
||||||
|
|
||||||
## Under the hood:
|
## Under the Hood
|
||||||
* The fff.h header file is generated by a ruby script
|
* The fff.h header file is generated by a ruby script
|
||||||
* There are tests under src/test
|
* There are tests under _./test_
|
||||||
* There is an example for testing an embedded UI and a hardware driver under src/examples
|
* There is an example for testing an embedded UI and a hardware driver under _./examples_
|
||||||
|
* There is an example of weak_linking under _./examples_
|
||||||
|
|
||||||
|
|
||||||
## Cheat Sheet
|
## Cheat Sheet
|
||||||
@@ -583,4 +654,6 @@ So whats the point?
|
|||||||
|-------|-------------|---------|
|
|-------|-------------|---------|
|
||||||
| FAKE_VOID_FUNC(fn [,arg_types*]); | Define a fake function named fn returning void with n arguments | FAKE_VOID_FUNC(DISPLAY_output_message, const char*); |
|
| FAKE_VOID_FUNC(fn [,arg_types*]); | Define a fake function named fn returning void with n arguments | FAKE_VOID_FUNC(DISPLAY_output_message, const char*); |
|
||||||
| FAKE_VALUE_FUNC(return_type, fn [,arg_types*]); | Define a fake function returning a value with type return_type taking n arguments | FAKE_VALUE_FUNC(int, DISPLAY_get_line_insert_index); |
|
| FAKE_VALUE_FUNC(return_type, fn [,arg_types*]); | Define a fake function returning a value with type return_type taking n arguments | FAKE_VALUE_FUNC(int, DISPLAY_get_line_insert_index); |
|
||||||
|
| FAKE_VOID_FUNC_VARARG(fn [,arg_types*], ...); | Define a fake variadic function returning void with type return_type taking n arguments and n variadic arguments | FAKE_VOID_FUNC_VARARG(fn, const char*, ...) |
|
||||||
|
| FAKE_VALUE_FUNC_VARARG(return_type, fn [,arg_types*], ...); | Define a fake variadic function returning a value with type return_type taking n arguments and n variadic arguments | FAKE_VALUE_FUNC_VARARG(int, fprintf, FILE*, const char*, ...) |
|
||||||
| RESET_FAKE(fn); | Reset the state of fake function called fn | RESET_FAKE(DISPLAY_init); |
|
| RESET_FAKE(fn); | Reset the state of fake function called fn | RESET_FAKE(DISPLAY_init); |
|
||||||
|
|||||||
25
buildandtest
25
buildandtest
@@ -1,16 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
cat LICENSE > fff.h
|
# Clear the environment
|
||||||
echo >> fff.h
|
rm -fr build fff.h
|
||||||
ruby fakegen.rb >> fff.h
|
mkdir build
|
||||||
make clean
|
|
||||||
make all
|
# Configure the build
|
||||||
build/fff_test_c
|
cmake -GNinja -B build || exit -1
|
||||||
build/fff_test_cpp --gtest_output=xml:build/test_results.xml
|
|
||||||
build/ui_test_ansic
|
# Build all targets
|
||||||
build/ui_test_cpp --gtest_output=xml:build/example_results.xml
|
cmake --build build || exit -1
|
||||||
build/fff_test_glob_c
|
|
||||||
build/fff_test_glob_cpp --gtest_output=xml:build/test_global_results.xml
|
# Run all tests
|
||||||
build/driver_testing --gtest_output=xml:build/driver_testing.xml
|
ctest --test-dir build --output-on-failure
|
||||||
build/driver_testing_fff --gtest_output=xml:build/driver_testing_fff.xml
|
|
||||||
|
|||||||
6
examples/CMakeLists.txt
Normal file
6
examples/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
add_subdirectory(driver_testing)
|
||||||
|
add_subdirectory(embedded_ui)
|
||||||
|
add_subdirectory(weak_linking)
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
all:
|
|
||||||
cd embedded_ui; $(MAKE) all
|
|
||||||
cd driver_testing; $(MAKE) all
|
|
||||||
|
|
||||||
clean:
|
|
||||||
cd embedded_ui; $(MAKE) clean
|
|
||||||
cd driver_testing; $(MAKE) clean
|
|
||||||
31
examples/driver_testing/CMakeLists.txt
Normal file
31
examples/driver_testing/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# Create the driver test binary
|
||||||
|
add_executable(driver_test
|
||||||
|
src/driver.c
|
||||||
|
src/driver.test.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(driver_test PRIVATE include)
|
||||||
|
target_link_libraries(driver_test PRIVATE gtest fff)
|
||||||
|
target_compile_definitions(driver_test PUBLIC TEST_USER_OWN_TR1_TUPLE=1 TESTING)
|
||||||
|
|
||||||
|
# Create the driver fff test binary
|
||||||
|
add_executable(driver_fff_test
|
||||||
|
src/driver.c
|
||||||
|
src/driver.test.fff.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(driver_fff_test PRIVATE include)
|
||||||
|
target_link_libraries(driver_fff_test PRIVATE gtest fff)
|
||||||
|
target_compile_definitions(driver_fff_test PUBLIC TEST_USER_OWN_TR1_TUPLE=1 TESTING)
|
||||||
|
|
||||||
|
# Add tests to ctest
|
||||||
|
add_test(
|
||||||
|
NAME driver_test
|
||||||
|
COMMAND $<TARGET_FILE:driver_test>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME driver_fff_test
|
||||||
|
COMMAND $<TARGET_FILE:driver_fff_test>
|
||||||
|
)
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
$(VERBOSE).SILENT:
|
|
||||||
|
|
||||||
BUILD_DIR = ../../build
|
|
||||||
TEMPLATE_PROGNAME = $(BUILD_DIR)/template
|
|
||||||
CPP_PROGNAME_NOFFF = $(BUILD_DIR)/driver_testing
|
|
||||||
CPP_PROGNAME_FFF = $(BUILD_DIR)/driver_testing_fff
|
|
||||||
CC = gcc
|
|
||||||
CC += -c
|
|
||||||
CPP = g++
|
|
||||||
CPP += -c
|
|
||||||
LD = g++
|
|
||||||
|
|
||||||
GTEST_OBJS = $(BUILD_DIR)/gtest-all.o $(BUILD_DIR)/gtest-main.o
|
|
||||||
C_OBJFILES = $(BUILD_DIR)/driver.o
|
|
||||||
TEMPLATE_OBJFILES = $(BUILD_DIR)/test_suite_template.o
|
|
||||||
FFF_OBJFILES = $(BUILD_DIR)/driver.test.fff.o $(GTEST_OBJS)
|
|
||||||
NOFFF_OBJFILES = $(BUILD_DIR)/driver.test.o $(GTEST_OBJS)
|
|
||||||
CPP_LIBS = -lpthread
|
|
||||||
|
|
||||||
|
|
||||||
all: $(CPP_PROGNAME_NOFFF) $(CPP_PROGNAME_FFF) $(TEMPLATE_PROGNAME)
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@echo "Cleaning object files"
|
|
||||||
@echo " rm -f $(BUILD_DIR)/*.o"
|
|
||||||
rm -f $(BUILD_DIR)/*.o
|
|
||||||
@echo "Cleaning backups"
|
|
||||||
@echo " rm -f *~"
|
|
||||||
rm -f *~
|
|
||||||
@echo "Removing programs"
|
|
||||||
@echo " rm -f $(CPP_PROGNAME_NOFFF) $(CPP_PROGNAME_FFF) $(TEMPLATE_PROGNAME)"
|
|
||||||
rm -f $(CPP_PROGNAME_NOFFF) $(CPP_PROGNAME_FFF) $(TEMPLATE_PROGNAME)
|
|
||||||
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.c
|
|
||||||
@echo "Compiling "$@
|
|
||||||
@echo " CC "$<
|
|
||||||
$(CC) -o $@ $< -DTESTING
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.cpp
|
|
||||||
@echo "Compiling "$@
|
|
||||||
@echo " CPP "$<
|
|
||||||
$(CPP) -DGTEST_USE_OWN_TR1_TUPLE=1 -I../.. -o $@ $< -DTESTING
|
|
||||||
|
|
||||||
$(TEMPLATE_PROGNAME): $(TEMPLATE_OBJFILES)
|
|
||||||
@echo "Linking "$@
|
|
||||||
@echo " LD -o "ctemplate" "$(TEMPLATE_OBJFILES)
|
|
||||||
$(LD) -o $(TEMPLATE_PROGNAME) $(TEMPLATE_OBJFILES)
|
|
||||||
|
|
||||||
$(CPP_PROGNAME_FFF): $(FFF_OBJFILES) $(C_OBJFILES)
|
|
||||||
@echo "Linking "$@
|
|
||||||
@echo " LD -o "$(CPP_PROGNAME_FFF)" "$(FFF_OBJFILES)
|
|
||||||
$(LD) -o $(CPP_PROGNAME_FFF) $(FFF_OBJFILES) $(C_OBJFILES) $(CPP_LIBS)
|
|
||||||
|
|
||||||
$(CPP_PROGNAME_NOFFF): $(NOFFF_OBJFILES) $(C_OBJFILES)
|
|
||||||
@echo "Linking "$@
|
|
||||||
@echo " LD -o "$(CPP_PROGNAME_NOFFF)" "$(NOFFF_OBJFILES)
|
|
||||||
$(LD) -o $(CPP_PROGNAME_NOFFF) $(NOFFF_OBJFILES) $(C_OBJFILES) $(CPP_LIBS)
|
|
||||||
|
|
||||||
nothing:
|
|
||||||
@echo "Nothing to do; quitting :("
|
|
||||||
@echo "HINT: Try make all"
|
|
||||||
@@ -3,7 +3,7 @@ extern "C"
|
|||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
#include "registers.h"
|
#include "registers.h"
|
||||||
}
|
}
|
||||||
#include "../../fff.h"
|
#include "../../../fff.h"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
extern "C"{
|
extern "C"{
|
||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
#include "registers.h"
|
#include "registers.h"
|
||||||
|
#include "hardware_abstraction.h"
|
||||||
}
|
}
|
||||||
#include "../../fff.h"
|
#include "fff.h"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
DEFINE_FFF_GLOBALS;
|
DEFINE_FFF_GLOBALS;
|
||||||
23
examples/embedded_ui/CMakeLists.txt
Normal file
23
examples/embedded_ui/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# Create the ui_test_ansic test binary
|
||||||
|
add_executable(ui_test_ansic src/UI_test_ansic.c src/UI.c)
|
||||||
|
target_include_directories(ui_test_ansic PRIVATE include)
|
||||||
|
target_link_libraries(ui_test_ansic PRIVATE fff)
|
||||||
|
|
||||||
|
# Create the ui_test_cpp test binary
|
||||||
|
add_executable(ui_test_cpp src/UI_test_cpp.cpp src/UI.c)
|
||||||
|
target_include_directories(ui_test_cpp PRIVATE include)
|
||||||
|
target_link_libraries(ui_test_cpp PRIVATE gtest fff)
|
||||||
|
|
||||||
|
# Add tests to ctest
|
||||||
|
add_test(
|
||||||
|
NAME ui_test_ansic
|
||||||
|
COMMAND $<TARGET_FILE:ui_test_ansic>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME ui_test_cpp
|
||||||
|
COMMAND $<TARGET_FILE:ui_test_cpp>
|
||||||
|
)
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
$(VERBOSE).SILENT:
|
|
||||||
|
|
||||||
BUILD_DIR = ../../build
|
|
||||||
TEMPLATE_PROGNAME = $(BUILD_DIR)/template
|
|
||||||
C_PROGNAME = $(BUILD_DIR)/ui_test_ansic
|
|
||||||
CPP_PROGNAME = $(BUILD_DIR)/ui_test_cpp
|
|
||||||
CC = gcc
|
|
||||||
CC += -c
|
|
||||||
CPP = g++
|
|
||||||
CPP += -c
|
|
||||||
LD = g++
|
|
||||||
|
|
||||||
GTEST_OBJS = $(BUILD_DIR)/gtest-all.o $(BUILD_DIR)/gtest-main.o
|
|
||||||
C_OBJFILES = $(BUILD_DIR)/UI_test_ansic.o $(BUILD_DIR)/UI.o
|
|
||||||
TEMPLATE_OBJFILES = $(BUILD_DIR)/test_suite_template.o
|
|
||||||
CPP_OBJFILES = $(BUILD_DIR)/UI_test_cpp.o $(BUILD_DIR)/UI.o $(GTEST_OBJS)
|
|
||||||
CPP_LIBS = -lpthread
|
|
||||||
|
|
||||||
|
|
||||||
all: $(C_PROGNAME) $(CPP_PROGNAME) $(TEMPLATE_PROGNAME)
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@echo "Cleaning object files"
|
|
||||||
@echo " rm -f $(BUILD_DIR)/*.o"
|
|
||||||
rm -f $(BUILD_DIR)/*.o
|
|
||||||
@echo "Cleaning backups"
|
|
||||||
@echo " rm -f *~"
|
|
||||||
rm -f *~
|
|
||||||
@echo "Removing programs"
|
|
||||||
@echo " rm -f "$(C_PROGNAME)
|
|
||||||
rm -f $(C_PROGNAME)
|
|
||||||
@echo " rm -f "$(CPP_PROGNAME) $(TEMPLATE_PROGNAME)
|
|
||||||
rm -f $(CPP_PROGNAME) $(TEMPLATE_PROGNAME)
|
|
||||||
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.c
|
|
||||||
@echo "Compiling "$@
|
|
||||||
@echo " CC "$<
|
|
||||||
$(CC) -o $@ $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.cpp
|
|
||||||
@echo "Compiling "$@
|
|
||||||
@echo " CPP "$<
|
|
||||||
$(CPP) -DGTEST_USE_OWN_TR1_TUPLE=1 -I../.. -o $@ $<
|
|
||||||
|
|
||||||
$(TEMPLATE_PROGNAME): $(TEMPLATE_OBJFILES)
|
|
||||||
@echo "Linking "$@
|
|
||||||
@echo " LD -o "ctemplate" "$(TEMPLATE_OBJFILES)
|
|
||||||
$(LD) -o $(TEMPLATE_PROGNAME) $(TEMPLATE_OBJFILES)
|
|
||||||
|
|
||||||
$(C_PROGNAME): $(C_OBJFILES)
|
|
||||||
@echo "Linking "$@
|
|
||||||
@echo " LD -o "$(C_PROGNAME)" "$(C_OBJFILES)
|
|
||||||
$(LD) -o $(C_PROGNAME) $(C_OBJFILES)
|
|
||||||
|
|
||||||
$(CPP_PROGNAME): $(CPP_OBJFILES) $(C_OBJFILES)
|
|
||||||
@echo "Linking "$@
|
|
||||||
@echo " LD -o "$(CPP_PROGNAME)" "$(CPP_OBJFILES)
|
|
||||||
$(LD) -o $(CPP_PROGNAME) $(CPP_OBJFILES) $(CPP_LIBS)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
nothing:
|
|
||||||
@echo "Nothing to do; quitting :("
|
|
||||||
@echo "HINT: Try make all"
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "UI.h"
|
#include "UI.h"
|
||||||
#include "../../fff.h"
|
#include "fff.h"
|
||||||
#include "SYSTEM.h"
|
#include "SYSTEM.h"
|
||||||
#include "DISPLAY.h"
|
#include "DISPLAY.h"
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ extern "C"{
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
#include "../../fff.h"
|
#include "fff.h"
|
||||||
DEFINE_FFF_GLOBALS;
|
DEFINE_FFF_GLOBALS;
|
||||||
|
|
||||||
/* SYSTEM.h */
|
/* SYSTEM.h */
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#include "../../test/c_test_framework.h"
|
|
||||||
|
|
||||||
/* Initialializers called for every test */
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tests go here */
|
|
||||||
TEST_F(GreeterTests, hello_world)
|
|
||||||
{
|
|
||||||
assert(1 == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
setbuf(stderr, NULL);
|
|
||||||
fprintf(stdout, "-------------\n");
|
|
||||||
fprintf(stdout, "Running Tests\n");
|
|
||||||
fprintf(stdout, "-------------\n\n");
|
|
||||||
fflush(0);
|
|
||||||
|
|
||||||
/* Run tests */
|
|
||||||
RUN_TEST(GreeterTests, hello_world);
|
|
||||||
|
|
||||||
|
|
||||||
printf("\n-------------\n");
|
|
||||||
printf("Complete\n");
|
|
||||||
printf("-------------\n\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
54
examples/weak_linking/CMakeLists.txt
Normal file
54
examples/weak_linking/CMakeLists.txt
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# Skip these tests for Windows
|
||||||
|
if(WIN32)
|
||||||
|
message(STATUS "Weak linking requires __attribute__((weak)) which isn't supported on MS VS, skipping tests")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set the global FFF_GCC_FUNCTION_ATTRIBUTES for config.h
|
||||||
|
set(FFF_GCC_FUNCTION_ATTRIBUTES "__attribute__((weak))")
|
||||||
|
configure_file(config/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
|
# Create a libfakes static library that will be used in the executables below.
|
||||||
|
# This library will depend on the above generated config.h which will add the
|
||||||
|
# FFF 'weak' function attributes.
|
||||||
|
add_library(libfakes STATIC
|
||||||
|
test/src/bus.fake.c
|
||||||
|
test/src/display.fake.c
|
||||||
|
test/src/error.fake.c
|
||||||
|
test/src/sensor.fake.c
|
||||||
|
test/src/test_common.c
|
||||||
|
)
|
||||||
|
target_precompile_headers(libfakes PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
target_include_directories(libfakes PUBLIC include test/include)
|
||||||
|
target_link_libraries(libfakes PUBLIC fff)
|
||||||
|
|
||||||
|
# Create the main test binary
|
||||||
|
add_executable(test_main src/main.c test/src/main.test.c)
|
||||||
|
target_link_libraries(test_main PRIVATE libfakes)
|
||||||
|
|
||||||
|
# Create the sensor test binary
|
||||||
|
add_executable(test_sensor src/sensor.c test/src/sensor.test.c)
|
||||||
|
target_link_libraries(test_sensor PRIVATE libfakes)
|
||||||
|
|
||||||
|
# Create the display test binary
|
||||||
|
add_executable(test_display src/display.c test/src/display.test.c ${LIBFAKES_SRCS})
|
||||||
|
target_link_libraries(test_display PRIVATE libfakes)
|
||||||
|
|
||||||
|
# Add tests to ctest
|
||||||
|
add_test(
|
||||||
|
NAME test_main
|
||||||
|
COMMAND $<TARGET_FILE:test_main>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME test_sensor
|
||||||
|
COMMAND $<TARGET_FILE:test_sensor>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME test_display
|
||||||
|
COMMAND $<TARGET_FILE:test_display>
|
||||||
|
)
|
||||||
5
examples/weak_linking/config/config.h.in
Normal file
5
examples/weak_linking/config/config.h.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/* Copyright 2022 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#cmakedefine FFF_GCC_FUNCTION_ATTRIBUTES @FFF_GCC_FUNCTION_ATTRIBUTES@
|
||||||
6
examples/weak_linking/include/bus.h
Normal file
6
examples/weak_linking/include/bus.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
bool bus_read_write( uint8_t dev, uint8_t registry, uint8_t* buffer, int len, bool assume_echo );
|
||||||
|
bool bus_write( uint8_t dev, uint8_t registry, const uint8_t* buffer, int len, bool assume_echo );
|
||||||
5
examples/weak_linking/include/display.h
Normal file
5
examples/weak_linking/include/display.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
bool display_init();
|
||||||
|
void display_update( const char* info );
|
||||||
7
examples/weak_linking/include/error.h
Normal file
7
examples/weak_linking/include/error.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
void runtime_error( const char* msg );
|
||||||
|
char* runtime_error_nice_print( const char* msg );
|
||||||
6
examples/weak_linking/include/sensor.h
Normal file
6
examples/weak_linking/include/sensor.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
bool sensor_init();
|
||||||
|
float sensor_read();
|
||||||
30
examples/weak_linking/src/bus.c
Normal file
30
examples/weak_linking/src/bus.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
uint16_t* BUS_REGISTER_BASE = (uint16_t*)0xDEADBEEF;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t action;
|
||||||
|
uint16_t reg;
|
||||||
|
uint8_t input;
|
||||||
|
uint8_t output
|
||||||
|
} BusDevice;
|
||||||
|
|
||||||
|
BusDevice* BUS = (BusDevice*)BUS_REGISTER_BASE;
|
||||||
|
|
||||||
|
bool bus_read_write( uint8_t dev, uint8_t registry, uint8_t* buffer, int len, bool assume_echo )
|
||||||
|
{
|
||||||
|
// Something that we dont want to run, since the emulation would be hard.
|
||||||
|
BUS->action = 0x01;
|
||||||
|
BUS->reg = registry;
|
||||||
|
for ( int loop =0 ; loop < len; loop ++ )
|
||||||
|
{
|
||||||
|
char output = buffer[loop];
|
||||||
|
BUS->output = output;
|
||||||
|
buffer[loop] = BUS->input;
|
||||||
|
|
||||||
|
if ( ( assume_echo ) && ( buffer[loop] != output ) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
29
examples/weak_linking/src/display.c
Normal file
29
examples/weak_linking/src/display.c
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "display.h"
|
||||||
|
#include "bus.h"
|
||||||
|
|
||||||
|
#define DISPLAY_ADDRESS 0xAF
|
||||||
|
#define DISPLAY_REG_INIT 0x10
|
||||||
|
#define DISPLAY_REG_UPDATE 0x20
|
||||||
|
|
||||||
|
bool display_init()
|
||||||
|
{
|
||||||
|
char init_config[] = { 0xDE, 0xFE, 0x00 };
|
||||||
|
bus_read_write( DISPLAY_ADDRESS, DISPLAY_REG_INIT, (uint8_t*)init_config, 3, false );
|
||||||
|
|
||||||
|
if (init_config[2] != 0x10)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_update( const char* info )
|
||||||
|
{
|
||||||
|
int len = strlen( info );
|
||||||
|
|
||||||
|
if ( bus_write( DISPLAY_ADDRESS, DISPLAY_REG_UPDATE, (const uint8_t*)info, len, true ) != true )
|
||||||
|
{
|
||||||
|
runtime_error("display update failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
10
examples/weak_linking/src/error.c
Normal file
10
examples/weak_linking/src/error.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
char* runtime_error_nice_print( const char* msg )
|
||||||
|
{
|
||||||
|
char* buffer = malloc(512);
|
||||||
|
snprintf(buffer, 512, "Got error: %s", msg );
|
||||||
|
buffer[511] = 0;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
27
examples/weak_linking/src/main.c
Normal file
27
examples/weak_linking/src/main.c
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "display.h"
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
void give_error( const char* msg )
|
||||||
|
{
|
||||||
|
char* buffer = runtime_error_nice_print( msg );
|
||||||
|
runtime_error( buffer );
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int update_main( void )
|
||||||
|
{
|
||||||
|
if ( !sensor_init() )
|
||||||
|
runtime_error("sensor init failed");
|
||||||
|
|
||||||
|
if ( !display_init() )
|
||||||
|
runtime_error("display init failed");
|
||||||
|
|
||||||
|
char buffer[32];
|
||||||
|
float reading = sensor_read();
|
||||||
|
snprintf( buffer, 32, "Sensor: %0.1f", reading );
|
||||||
|
display_update( buffer );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
29
examples/weak_linking/src/sensor.c
Normal file
29
examples/weak_linking/src/sensor.c
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "sensor.h"
|
||||||
|
#include "bus.h"
|
||||||
|
|
||||||
|
#define SENSOR_ADDRESS 0xAA
|
||||||
|
#define SENSOR_REG_READ 0xF0
|
||||||
|
|
||||||
|
float LOCAL_buffer[4];
|
||||||
|
|
||||||
|
bool sensor_init()
|
||||||
|
{
|
||||||
|
memset( LOCAL_buffer, 0x00, sizeof(LOCAL_buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sensor_read()
|
||||||
|
{
|
||||||
|
char data[4] = { 0xFF, 0xFF, 0x00, 0x00 };
|
||||||
|
bus_read_write( SENSOR_ADDRESS, SENSOR_REG_READ, (uint8_t*)&data, 4, false );
|
||||||
|
|
||||||
|
if (data[0] != 0x10)
|
||||||
|
{
|
||||||
|
runtime_error("sensor read failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
float ret_value = data[1] + data[2] / 256.0;
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
10
examples/weak_linking/test/include/bus.fake.h
Normal file
10
examples/weak_linking/test/include/bus.fake.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _AUTOFAKE_BUS_H
|
||||||
|
#define _AUTOFAKE_BUS_H
|
||||||
|
|
||||||
|
#include "fff.h"
|
||||||
|
#include "bus.h"
|
||||||
|
|
||||||
|
DECLARE_FAKE_VALUE_FUNC( bool, bus_read_write, uint8_t, uint8_t, uint8_t*, int, bool );
|
||||||
|
DECLARE_FAKE_VALUE_FUNC( bool, bus_write, uint8_t, uint8_t, const uint8_t*, int, bool );
|
||||||
|
|
||||||
|
#endif // _AUTOFAKE_BUS_H
|
||||||
10
examples/weak_linking/test/include/display.fake.h
Normal file
10
examples/weak_linking/test/include/display.fake.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _AUTOFAKE_DISPLAY_H
|
||||||
|
#define _AUTOFAKE_DISPLAY_H
|
||||||
|
|
||||||
|
#include "fff.h"
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
DECLARE_FAKE_VALUE_FUNC( bool, display_init );
|
||||||
|
DECLARE_FAKE_VOID_FUNC( display_update, const char* );
|
||||||
|
|
||||||
|
#endif // _AUTOFAKE_DISPLAY_H
|
||||||
10
examples/weak_linking/test/include/error.fake.h
Normal file
10
examples/weak_linking/test/include/error.fake.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _AUTOFAKE_ERROR_H
|
||||||
|
#define _AUTOFAKE_ERROR_H
|
||||||
|
|
||||||
|
#include "fff.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
DECLARE_FAKE_VOID_FUNC( runtime_error, const char* );
|
||||||
|
DECLARE_FAKE_VALUE_FUNC( char*, runtime_error_nice_print, const char* );
|
||||||
|
|
||||||
|
#endif // _AUTOFAKE_ERROR_H
|
||||||
13
examples/weak_linking/test/include/sensor.fake.h
Normal file
13
examples/weak_linking/test/include/sensor.fake.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
#ifndef _AUTOMOCK_SENSOR_H
|
||||||
|
#define _AUTOMOCK_SENSOR_H
|
||||||
|
|
||||||
|
#include "fff.h"
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
DECLARE_FAKE_VALUE_FUNC( bool, sensor_init );
|
||||||
|
DECLARE_FAKE_VALUE_FUNC( float, sensor_read );
|
||||||
|
|
||||||
|
void TEST_sensor_generate_data( char* buffer, float value );
|
||||||
|
|
||||||
|
#endif // _AUTOMOCK_SENSOR_H
|
||||||
13
examples/weak_linking/test/include/test_common.h
Normal file
13
examples/weak_linking/test/include/test_common.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "fff.h"
|
||||||
|
#include "bus.fake.h"
|
||||||
|
#include "error.fake.h"
|
||||||
|
|
||||||
|
|
||||||
|
void init_tests();
|
||||||
|
|
||||||
|
extern char GLOBAL_TEST_bus_read_ret[32];
|
||||||
4
examples/weak_linking/test/src/bus.fake.c
Normal file
4
examples/weak_linking/test/src/bus.fake.c
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#include "bus.fake.h"
|
||||||
|
|
||||||
|
DEFINE_FAKE_VALUE_FUNC( bool, bus_read_write, uint8_t, uint8_t, uint8_t*, int, bool );
|
||||||
|
DEFINE_FAKE_VALUE_FUNC( bool, bus_write, uint8_t, uint8_t, const uint8_t*, int, bool );
|
||||||
4
examples/weak_linking/test/src/display.fake.c
Normal file
4
examples/weak_linking/test/src/display.fake.c
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#include "display.fake.h"
|
||||||
|
|
||||||
|
DEFINE_FAKE_VALUE_FUNC( bool, display_init );
|
||||||
|
DEFINE_FAKE_VOID_FUNC( display_update, const char* );
|
||||||
24
examples/weak_linking/test/src/display.test.c
Normal file
24
examples/weak_linking/test/src/display.test.c
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "test_common.h"
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
DEFINE_FFF_GLOBALS;
|
||||||
|
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
init_tests(); // Resets common and hook errors to asserts.
|
||||||
|
|
||||||
|
GLOBAL_TEST_bus_read_ret[2] = 0x10;
|
||||||
|
assert( display_init() == true );
|
||||||
|
assert( bus_read_write_fake.call_count == 1);
|
||||||
|
|
||||||
|
display_update( "TEST INFO" );
|
||||||
|
assert( bus_read_write_fake.call_count == 1 );
|
||||||
|
assert( bus_write_fake.call_count == 1 );
|
||||||
|
|
||||||
|
GLOBAL_TEST_bus_read_ret[2] = 0x00;
|
||||||
|
assert( display_init() == false );
|
||||||
|
|
||||||
|
printf("Test " __FILE__ " ok\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
4
examples/weak_linking/test/src/error.fake.c
Normal file
4
examples/weak_linking/test/src/error.fake.c
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#include "error.fake.h"
|
||||||
|
|
||||||
|
DEFINE_FAKE_VOID_FUNC( runtime_error, const char* );
|
||||||
|
DEFINE_FAKE_VALUE_FUNC( char*, runtime_error_nice_print, const char* );
|
||||||
26
examples/weak_linking/test/src/main.test.c
Normal file
26
examples/weak_linking/test/src/main.test.c
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
#include "display.fake.h"
|
||||||
|
#include "sensor.fake.h"
|
||||||
|
#include "test_common.h"
|
||||||
|
|
||||||
|
DEFINE_FFF_GLOBALS;
|
||||||
|
|
||||||
|
|
||||||
|
int update_main( void );
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
init_tests(); // Resets common and hook errors to asserts.
|
||||||
|
|
||||||
|
sensor_init_fake.return_val = true;
|
||||||
|
display_init_fake.return_val = true;
|
||||||
|
|
||||||
|
update_main();
|
||||||
|
|
||||||
|
assert( sensor_init_fake.call_count == 1 );
|
||||||
|
assert( display_init_fake.call_count == 1 );
|
||||||
|
assert( display_update_fake.call_count == 1 );
|
||||||
|
|
||||||
|
printf("Test " __FILE__ " ok\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
11
examples/weak_linking/test/src/sensor.fake.c
Normal file
11
examples/weak_linking/test/src/sensor.fake.c
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include "sensor.fake.h"
|
||||||
|
|
||||||
|
DEFINE_FAKE_VALUE_FUNC( bool, sensor_init );
|
||||||
|
DEFINE_FAKE_VALUE_FUNC( float, sensor_read );
|
||||||
|
|
||||||
|
void TEST_sensor_generate_data( char* buffer, float value )
|
||||||
|
{
|
||||||
|
buffer[0] = 0x10;
|
||||||
|
buffer[1] = (int)(value);
|
||||||
|
buffer[2] = (value - buffer[1])*255;
|
||||||
|
}
|
||||||
23
examples/weak_linking/test/src/sensor.test.c
Normal file
23
examples/weak_linking/test/src/sensor.test.c
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "test_common.h"
|
||||||
|
|
||||||
|
#include "sensor.h"
|
||||||
|
#include "sensor.fake.h"
|
||||||
|
|
||||||
|
DEFINE_FFF_GLOBALS;
|
||||||
|
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
init_tests();
|
||||||
|
|
||||||
|
assert( sensor_init() == true );
|
||||||
|
|
||||||
|
TEST_sensor_generate_data( GLOBAL_TEST_bus_read_ret, 1.5f );
|
||||||
|
float value = sensor_read();
|
||||||
|
float value_error = value - 1.5f;
|
||||||
|
assert( value_error < 0.1f && value_error > -0.1f );
|
||||||
|
printf("Test " __FILE__ " ok\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
33
examples/weak_linking/test/src/test_common.c
Normal file
33
examples/weak_linking/test/src/test_common.c
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include "test_common.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
char GLOBAL_TEST_bus_read_ret[32];
|
||||||
|
|
||||||
|
|
||||||
|
void spoof_runtime_error( const char* info )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Runtime error: %s\n", info );
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool spoof_bus_read_write( uint8_t dev, uint8_t registry, uint8_t* buffer, int len, bool assume_echo )
|
||||||
|
{
|
||||||
|
memcpy( buffer, GLOBAL_TEST_bus_read_ret, len );
|
||||||
|
fprintf(stderr, "bus spoof %d %d\n", (int)dev, (int)registry );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_tests()
|
||||||
|
{
|
||||||
|
memset( GLOBAL_TEST_bus_read_ret, 0x00, sizeof(GLOBAL_TEST_bus_read_ret));
|
||||||
|
FFF_RESET_HISTORY();
|
||||||
|
|
||||||
|
RESET_FAKE(bus_read_write);
|
||||||
|
RESET_FAKE(bus_write);
|
||||||
|
RESET_FAKE(runtime_error);
|
||||||
|
|
||||||
|
runtime_error_fake.custom_fake = spoof_runtime_error;
|
||||||
|
bus_read_write_fake.custom_fake = spoof_bus_read_write;
|
||||||
|
bus_write_fake.return_val = true;
|
||||||
|
}
|
||||||
254
fakegen.rb
254
fakegen.rb
@@ -8,6 +8,10 @@ $MAX_ARGS = 20
|
|||||||
$DEFAULT_ARG_HISTORY = 50
|
$DEFAULT_ARG_HISTORY = 50
|
||||||
$MAX_CALL_HISTORY = 50
|
$MAX_CALL_HISTORY = 50
|
||||||
|
|
||||||
|
def license
|
||||||
|
File.foreach("#{__dir__}/LICENSE") { |line| putd line }
|
||||||
|
end
|
||||||
|
|
||||||
def include_dependencies
|
def include_dependencies
|
||||||
putd "#include <stdarg.h>"
|
putd "#include <stdarg.h>"
|
||||||
putd "#include <string.h> /* For memset and memcpy */"
|
putd "#include <string.h> /* For memset and memcpy */"
|
||||||
@@ -26,6 +30,12 @@ def output_constants
|
|||||||
putd "#define FFF_CALL_HISTORY_LEN (#{$MAX_CALL_HISTORY}u)"
|
putd "#define FFF_CALL_HISTORY_LEN (#{$MAX_CALL_HISTORY}u)"
|
||||||
}
|
}
|
||||||
putd "#endif"
|
putd "#endif"
|
||||||
|
putd "#ifndef FFF_GCC_FUNCTION_ATTRIBUTES"
|
||||||
|
indent {
|
||||||
|
putd "#define FFF_GCC_FUNCTION_ATTRIBUTES"
|
||||||
|
}
|
||||||
|
putd "#endif"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -259,7 +269,7 @@ def indent
|
|||||||
popd
|
popd
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_macro(arg_count, has_varargs, is_value_function)
|
def output_macro(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
|
|
||||||
vararg_name = has_varargs ? "_VARARG" : ""
|
vararg_name = has_varargs ? "_VARARG" : ""
|
||||||
fake_macro_name = is_value_function ? "FAKE_VALUE_FUNC#{arg_count}#{vararg_name}" : "FAKE_VOID_FUNC#{arg_count}#{vararg_name}"
|
fake_macro_name = is_value_function ? "FAKE_VALUE_FUNC#{arg_count}#{vararg_name}" : "FAKE_VOID_FUNC#{arg_count}#{vararg_name}"
|
||||||
@@ -269,53 +279,50 @@ def output_macro(arg_count, has_varargs, is_value_function)
|
|||||||
return_type = is_value_function ? "RETURN_TYPE" : ""
|
return_type = is_value_function ? "RETURN_TYPE" : ""
|
||||||
|
|
||||||
puts
|
puts
|
||||||
output_macro_header(declare_macro_name, saved_arg_count, has_varargs, return_type)
|
output_macro_header(declare_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
indent {
|
indent {
|
||||||
extern_c { # define argument capture variables
|
output_variables(saved_arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
output_variables(saved_arg_count, has_varargs, is_value_function)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
puts
|
puts
|
||||||
output_macro_header(define_macro_name, saved_arg_count, has_varargs, return_type)
|
output_macro_header(define_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
indent {
|
indent {
|
||||||
extern_c {
|
putd_backslash "FUNCNAME##_Fake FUNCNAME##_fake;"
|
||||||
putd_backslash "FUNCNAME##_Fake FUNCNAME##_fake;"
|
putd_backslash function_signature(saved_arg_count, has_varargs, has_calling_conventions, is_value_function) + "{"
|
||||||
putd_backslash function_signature(saved_arg_count, has_varargs, is_value_function) + "{"
|
indent {
|
||||||
indent {
|
output_function_body(saved_arg_count, has_varargs, is_value_function)
|
||||||
output_function_body(saved_arg_count, has_varargs, is_value_function)
|
|
||||||
}
|
|
||||||
putd_backslash "}"
|
|
||||||
putd_backslash "DEFINE_RESET_FUNCTION(FUNCNAME)"
|
|
||||||
}
|
}
|
||||||
|
putd_backslash "}"
|
||||||
|
putd_backslash "DEFINE_RESET_FUNCTION(FUNCNAME)"
|
||||||
}
|
}
|
||||||
|
|
||||||
puts
|
puts
|
||||||
|
|
||||||
output_macro_header(fake_macro_name, saved_arg_count, has_varargs, return_type)
|
output_macro_header(fake_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
indent {
|
indent {
|
||||||
putd macro_signature_for(declare_macro_name, saved_arg_count, has_varargs, return_type)
|
putd macro_signature_for(declare_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
putd macro_signature_for(define_macro_name, saved_arg_count, has_varargs, return_type)
|
putd macro_signature_for(define_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
puts
|
puts
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_macro_header(macro_name, arg_count, has_varargs, return_type)
|
def output_macro_header(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
output_macro_name(macro_name, arg_count, has_varargs, return_type)
|
output_macro_name(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
# #define #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...)
|
# #define #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...)
|
||||||
def output_macro_name(macro_name, arg_count, has_varargs, return_type)
|
def output_macro_name(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
putd "#define " + macro_signature_for(macro_name, arg_count, has_varargs, return_type)
|
putd "#define " + macro_signature_for(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
# #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...) \
|
# #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...) \
|
||||||
def macro_signature_for(macro_name, arg_count, has_varargs, return_type)
|
def macro_signature_for(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
||||||
parameter_list = "#{macro_name}("
|
parameter_list = "#{macro_name}("
|
||||||
if return_type != ""
|
if return_type != ""
|
||||||
parameter_list += return_type
|
parameter_list += return_type
|
||||||
parameter_list += ", "
|
parameter_list += ", "
|
||||||
end
|
end
|
||||||
|
parameter_list += "CALLING_CONVENTION, " if (has_calling_conventions)
|
||||||
parameter_list += "FUNCNAME"
|
parameter_list += "FUNCNAME"
|
||||||
|
|
||||||
arg_count.times { |i| parameter_list += ", ARG#{i}_TYPE" }
|
arg_count.times { |i| parameter_list += ", ARG#{i}_TYPE" }
|
||||||
@@ -327,7 +334,7 @@ def macro_signature_for(macro_name, arg_count, has_varargs, return_type)
|
|||||||
parameter_list
|
parameter_list
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_variables(arg_count, has_varargs, is_value_function)
|
def output_variables(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
in_struct{
|
in_struct{
|
||||||
arg_count.times { |argN|
|
arg_count.times { |argN|
|
||||||
putd_backslash "DECLARE_ARG(ARG#{argN}_TYPE, #{argN}, FUNCNAME)"
|
putd_backslash "DECLARE_ARG(ARG#{argN}_TYPE, #{argN}, FUNCNAME)"
|
||||||
@@ -336,12 +343,12 @@ def output_variables(arg_count, has_varargs, is_value_function)
|
|||||||
putd_backslash "DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)" unless not is_value_function
|
putd_backslash "DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)" unless not is_value_function
|
||||||
putd_backslash "DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)" unless not is_value_function
|
putd_backslash "DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)" unless not is_value_function
|
||||||
putd_backslash "DECLARE_CUSTOM_FAKE_SEQ_VARIABLES"
|
putd_backslash "DECLARE_CUSTOM_FAKE_SEQ_VARIABLES"
|
||||||
output_custom_function_signature(arg_count, has_varargs, is_value_function)
|
output_custom_function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
output_custom_function_array(arg_count, has_varargs, is_value_function)
|
output_custom_function_array(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
}
|
}
|
||||||
putd_backslash "extern FUNCNAME##_Fake FUNCNAME##_fake;"
|
putd_backslash "extern FUNCNAME##_Fake FUNCNAME##_fake;"
|
||||||
putd_backslash "void FUNCNAME##_reset(void);"
|
putd_backslash "void FUNCNAME##_reset(void);"
|
||||||
putd_backslash function_signature(arg_count, has_varargs, is_value_function) + ";"
|
putd_backslash function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function) + ";"
|
||||||
end
|
end
|
||||||
|
|
||||||
#example: ARG0_TYPE arg0, ARG1_TYPE arg1
|
#example: ARG0_TYPE arg0, ARG1_TYPE arg1
|
||||||
@@ -360,26 +367,35 @@ def arg_list(args_count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# RETURN_TYPE (*custom_fake)(ARG0_TYPE arg0);\
|
# RETURN_TYPE (*custom_fake)(ARG0_TYPE arg0);\
|
||||||
|
# OR
|
||||||
|
# RETURN_TYPE (CALLING_CONVENTION *custom_fake)(ARG0_TYPE arg0);\
|
||||||
|
#
|
||||||
# void (*custom_fake)(ARG0_TYPE arg0, ARG1_TYPE arg1, ARG2_TYPE arg2);\
|
# void (*custom_fake)(ARG0_TYPE arg0, ARG1_TYPE arg1, ARG2_TYPE arg2);\
|
||||||
def output_custom_function_signature(arg_count, has_varargs, is_value_function)
|
def output_custom_function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
||||||
ap_list = has_varargs ? ", va_list ap" : ""
|
ap_list = has_varargs ? ", va_list ap" : ""
|
||||||
signature = "(*custom_fake)(#{arg_val_list(arg_count)}#{ap_list});"
|
signature = has_calling_conventions ? "(CALLING_CONVENTION *custom_fake)" : "(*custom_fake)"
|
||||||
|
signature += "(#{arg_val_list(arg_count)}#{ap_list});"
|
||||||
putd_backslash return_type + signature
|
putd_backslash return_type + signature
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_custom_function_array(arg_count, has_varargs, is_value_function)
|
def output_custom_function_array(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
||||||
ap_list = has_varargs ? ", va_list ap" : ""
|
ap_list = has_varargs ? ", va_list ap" : ""
|
||||||
custom_array = "(**custom_fake_seq)(#{arg_val_list(arg_count)}#{ap_list});"
|
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
|
putd_backslash return_type + custom_array
|
||||||
end
|
end
|
||||||
|
|
||||||
# example: RETURN_TYPE FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
# example: RETURN_TYPE FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
||||||
def function_signature(arg_count, has_varargs, is_value_function)
|
# OR
|
||||||
|
# RETURN_TYPE CALLING_CONVENTION FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
||||||
|
def function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
||||||
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
||||||
varargs = has_varargs ? ", ..." : ""
|
varargs = has_varargs ? ", ..." : ""
|
||||||
"#{return_type} FUNCNAME(#{arg_val_list(arg_count)}#{varargs})"
|
calling_conventions = has_calling_conventions ?
|
||||||
|
"#{return_type} FFF_GCC_FUNCTION_ATTRIBUTES CALLING_CONVENTION FUNCNAME(#{arg_val_list(arg_count)}#{varargs})" :
|
||||||
|
"#{return_type} FFF_GCC_FUNCTION_ATTRIBUTES FUNCNAME(#{arg_val_list(arg_count)}#{varargs})"
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_function_body(arg_count, has_varargs, is_value_function)
|
def output_function_body(arg_count, has_varargs, is_value_function)
|
||||||
@@ -398,6 +414,35 @@ def output_function_body(arg_count, has_varargs, is_value_function)
|
|||||||
putd_backslash "REGISTER_CALL(FUNCNAME);"
|
putd_backslash "REGISTER_CALL(FUNCNAME);"
|
||||||
|
|
||||||
if has_varargs
|
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){"
|
putd_backslash "if(FUNCNAME##_fake.custom_fake){"
|
||||||
indent {
|
indent {
|
||||||
putd_backslash "RETURN_TYPE ret;" if is_value_function
|
putd_backslash "RETURN_TYPE ret;" if is_value_function
|
||||||
@@ -438,7 +483,14 @@ def output_function_body(arg_count, has_varargs, is_value_function)
|
|||||||
putd_backslash "}"
|
putd_backslash "}"
|
||||||
}
|
}
|
||||||
putd_backslash "}"
|
putd_backslash "}"
|
||||||
putd_backslash "if (FUNCNAME##_fake.custom_fake) #{return_type}FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});"
|
putd_backslash "if (FUNCNAME##_fake.custom_fake){ "
|
||||||
|
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
|
||||||
|
putd_backslash "return ret;" unless not is_value_function
|
||||||
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});"
|
||||||
|
}
|
||||||
|
putd_backslash "}"
|
||||||
end
|
end
|
||||||
|
|
||||||
putd_backslash "RETURN_FAKE_RESULT(FUNCNAME)" if is_value_function
|
putd_backslash "RETURN_FAKE_RESULT(FUNCNAME)" if is_value_function
|
||||||
@@ -453,9 +505,9 @@ def define_fff_globals
|
|||||||
}
|
}
|
||||||
putd "} fff_globals_t;"
|
putd "} fff_globals_t;"
|
||||||
puts
|
puts
|
||||||
putd_backslash "FFF_EXTERN_C"
|
putd "FFF_EXTERN_C"
|
||||||
putd "extern fff_globals_t fff;"
|
putd "extern fff_globals_t fff;"
|
||||||
putd_backslash "FFF_END_EXTERN_C"
|
putd "FFF_END_EXTERN_C"
|
||||||
puts
|
puts
|
||||||
putd_backslash "#define DEFINE_FFF_GLOBALS"
|
putd_backslash "#define DEFINE_FFF_GLOBALS"
|
||||||
indent {
|
indent {
|
||||||
@@ -466,25 +518,21 @@ def define_fff_globals
|
|||||||
putd "FFF_END_EXTERN_C"
|
putd "FFF_END_EXTERN_C"
|
||||||
}
|
}
|
||||||
puts
|
puts
|
||||||
putd "#define FFF_RESET_HISTORY() fff.call_history_idx = 0;"
|
putd_backslash "#define FFF_RESET_HISTORY()"
|
||||||
|
indent {
|
||||||
|
putd_backslash "fff.call_history_idx = 0;"
|
||||||
|
putd "memset(fff.call_history, 0, sizeof(fff.call_history));"
|
||||||
|
}
|
||||||
puts
|
puts
|
||||||
putd_backslash "#define REGISTER_CALL(function)"
|
putd_backslash "#define REGISTER_CALL(function)"
|
||||||
indent {
|
indent {
|
||||||
putd_backslash "if(fff.call_history_idx < FFF_CALL_HISTORY_LEN)"
|
putd_backslash "if(fff.call_history_idx < FFF_CALL_HISTORY_LEN)"
|
||||||
indent {
|
indent {
|
||||||
putd "fff.call_history[fff.call_history_idx++] = (fff_function_t)function;"
|
putd "fff.call_history[fff.call_history_idx++] = (fff_function_t)function;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def extern_c
|
|
||||||
putd_backslash "FFF_EXTERN_C"
|
|
||||||
indent {
|
|
||||||
yield
|
|
||||||
}
|
|
||||||
putd_backslash "FFF_END_EXTERN_C"
|
|
||||||
end
|
|
||||||
|
|
||||||
def in_struct
|
def in_struct
|
||||||
putd_backslash "typedef struct FUNCNAME##_Fake {"
|
putd_backslash "typedef struct FUNCNAME##_Fake {"
|
||||||
indent {
|
indent {
|
||||||
@@ -505,18 +553,22 @@ def include_guard
|
|||||||
end
|
end
|
||||||
|
|
||||||
def msvc_expand_macro_fix
|
def msvc_expand_macro_fix
|
||||||
putd "/* MSVC expand macro fix */"
|
putd "/* MSVC expand macro fix */"
|
||||||
putd "#define EXPAND(x) x"
|
putd "#define EXPAND(x) x"
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_arg_sequence(args, prefix, do_reverse, joinstr)
|
def generate_arg_sequence(args, prefix, do_reverse, joinstr)
|
||||||
fmap = (0..args).flat_map {|i| [prefix + i.to_s]}
|
fmap = (0..args).flat_map {|i| [prefix + i.to_s]}
|
||||||
if do_reverse then fmap.reverse.join(joinstr) else fmap.join(", ") end
|
if do_reverse then fmap.reverse.join(joinstr) else fmap.join(", ") end
|
||||||
end
|
end
|
||||||
|
|
||||||
def counting_macro_instance(type, vararg = :non_vararg, prefix = "")
|
def counting_macro_instance(type, has_calling_conventions, vararg = :non_vararg, prefix = "")
|
||||||
appendix = (vararg == :vararg) ? "_VARARG" : ""
|
appendix = (vararg == :vararg) ? "_VARARG" : ""
|
||||||
minus_count = (type == :VOID) ? 1 : 2
|
if has_calling_conventions
|
||||||
|
minus_count = (type == :VOID) ? 2 : 3
|
||||||
|
else
|
||||||
|
minus_count = (type == :VOID) ? 1 : 2
|
||||||
|
end
|
||||||
|
|
||||||
<<-MACRO_COUNTING_INSTANCE
|
<<-MACRO_COUNTING_INSTANCE
|
||||||
#define #{prefix}FAKE_#{type.to_s}_FUNC#{appendix}(...) \
|
#define #{prefix}FAKE_#{type.to_s}_FUNC#{appendix}(...) \
|
||||||
@@ -531,78 +583,96 @@ def counting_macro_instance(type, vararg = :non_vararg, prefix = "")
|
|||||||
MACRO_COUNTING_INSTANCE
|
MACRO_COUNTING_INSTANCE
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_macro_counting_shortcuts
|
def output_macro_counting_shortcuts(has_calling_conventions)
|
||||||
|
has_calling_conventions ?
|
||||||
|
(arg_depth = ["3", "2"]; calling_conv = "callingConv, ") :
|
||||||
|
(arg_depth = ["2", "1"]; calling_conv = "")
|
||||||
|
|
||||||
msvc_expand_macro_fix
|
msvc_expand_macro_fix
|
||||||
putd <<-MACRO_COUNTING
|
putd <<-MACRO_COUNTING
|
||||||
|
|
||||||
#define PP_NARG_MINUS2(...) \
|
#define PP_NARG_MINUS#{arg_depth[0]}(...) \
|
||||||
EXPAND(PP_NARG_MINUS2_(__VA_ARGS__, PP_RSEQ_N_MINUS2()))
|
EXPAND(PP_NARG_MINUS#{arg_depth[0]}_(__VA_ARGS__, PP_RSEQ_N_MINUS#{arg_depth[0]}()))
|
||||||
|
|
||||||
#define PP_NARG_MINUS2_(...) \
|
#define PP_NARG_MINUS#{arg_depth[0]}_(...) \
|
||||||
EXPAND(PP_ARG_MINUS2_N(__VA_ARGS__))
|
EXPAND(PP_ARG_MINUS#{arg_depth[0]}_N(__VA_ARGS__))
|
||||||
|
|
||||||
#define PP_ARG_MINUS2_N(returnVal, #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
#define PP_ARG_MINUS#{arg_depth[0]}_N(returnVal, #{calling_conv} #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
||||||
|
|
||||||
#define PP_RSEQ_N_MINUS2() \
|
#define PP_RSEQ_N_MINUS#{arg_depth[0]}() \
|
||||||
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
||||||
|
|
||||||
|
#define PP_NARG_MINUS#{arg_depth[1]}(...) \
|
||||||
|
EXPAND(PP_NARG_MINUS#{arg_depth[1]}_(__VA_ARGS__, PP_RSEQ_N_MINUS#{arg_depth[1]}()))
|
||||||
|
|
||||||
#define PP_NARG_MINUS1(...) \
|
#define PP_NARG_MINUS#{arg_depth[1]}_(...) \
|
||||||
EXPAND(PP_NARG_MINUS1_(__VA_ARGS__, PP_RSEQ_N_MINUS1()))
|
EXPAND(PP_ARG_MINUS#{arg_depth[1]}_N(__VA_ARGS__))
|
||||||
|
|
||||||
#define PP_NARG_MINUS1_(...) \
|
#define PP_ARG_MINUS#{arg_depth[1]}_N(#{calling_conv} #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
||||||
EXPAND(PP_ARG_MINUS1_N(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define PP_ARG_MINUS1_N(#{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
#define PP_RSEQ_N_MINUS#{arg_depth[1]}() \
|
||||||
|
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
||||||
#define PP_RSEQ_N_MINUS1() \
|
|
||||||
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* DECLARE AND DEFINE FAKE FUNCTIONS - PLACE IN TEST FILES */
|
/* DECLARE AND DEFINE FAKE FUNCTIONS - PLACE IN TEST FILES */
|
||||||
|
|
||||||
#{counting_macro_instance(:VALUE)}
|
#{counting_macro_instance(:VALUE, has_calling_conventions)}
|
||||||
#{counting_macro_instance(:VOID)}
|
#{counting_macro_instance(:VOID, has_calling_conventions)}
|
||||||
#{counting_macro_instance(:VALUE, :vararg)}
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg)}
|
||||||
#{counting_macro_instance(:VOID, :vararg)}
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg)}
|
||||||
|
|
||||||
/* DECLARE FAKE FUNCTIONS - PLACE IN HEADER FILES */
|
/* DECLARE FAKE FUNCTIONS - PLACE IN HEADER FILES */
|
||||||
|
|
||||||
#{counting_macro_instance(:VALUE, :non_vararg, "DECLARE_")}
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :non_vararg, "DECLARE_")}
|
||||||
#{counting_macro_instance(:VOID, :non_vararg, "DECLARE_")}
|
#{counting_macro_instance(:VOID, has_calling_conventions, :non_vararg, "DECLARE_")}
|
||||||
#{counting_macro_instance(:VALUE, :vararg, "DECLARE_")}
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg, "DECLARE_")}
|
||||||
#{counting_macro_instance(:VOID, :vararg, "DECLARE_")}
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg, "DECLARE_")}
|
||||||
|
|
||||||
/* DEFINE FAKE FUNCTIONS - PLACE IN SOURCE FILES */
|
/* DEFINE FAKE FUNCTIONS - PLACE IN SOURCE FILES */
|
||||||
|
|
||||||
#{counting_macro_instance(:VALUE, :non_vararg, "DEFINE_")}
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :non_vararg, "DEFINE_")}
|
||||||
#{counting_macro_instance(:VOID, :non_vararg, "DEFINE_")}
|
#{counting_macro_instance(:VOID, has_calling_conventions, :non_vararg, "DEFINE_")}
|
||||||
#{counting_macro_instance(:VALUE, :vararg, "DEFINE_")}
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg, "DEFINE_")}
|
||||||
#{counting_macro_instance(:VOID, :vararg, "DEFINE_")}
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg, "DEFINE_")}
|
||||||
|
|
||||||
MACRO_COUNTING
|
MACRO_COUNTING
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_c_and_cpp
|
def output_c_and_cpp(has_calling_conventions)
|
||||||
|
license
|
||||||
include_guard {
|
include_guard {
|
||||||
include_dependencies
|
include_dependencies
|
||||||
output_constants
|
output_constants
|
||||||
output_internal_helper_macros
|
output_internal_helper_macros
|
||||||
yield
|
yield
|
||||||
output_macro_counting_shortcuts
|
output_macro_counting_shortcuts(has_calling_conventions)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# lets generate!!
|
def help
|
||||||
output_c_and_cpp{
|
# Check if we should generate _with_ support for specifying calling conventions
|
||||||
define_fff_globals
|
if (ARGV[0] == "--help" or ARGV[0] == "-h")
|
||||||
# Create fake generators for 0..MAX_ARGS
|
puts "Usage: fakegen.rb [options]
|
||||||
num_fake_generators = $MAX_ARGS + 1
|
-h, --help Show this help message
|
||||||
num_fake_generators.times {|arg_count| output_macro(arg_count, false, false)}
|
-wcc, --with-calling-conventions Support specifying calling conventions"
|
||||||
num_fake_generators.times {|arg_count| output_macro(arg_count, false, true)}
|
exit
|
||||||
# generate the varargs variants
|
end
|
||||||
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, false)}
|
yield
|
||||||
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, true)}
|
end
|
||||||
|
|
||||||
|
help {
|
||||||
|
# Determine if we should generate with support for calling conventions
|
||||||
|
has_calling_conventions = true if (ARGV[0] == "--with-calling-conventions" or ARGV[0] == "-wcc")
|
||||||
|
# lets generate!!
|
||||||
|
output_c_and_cpp(has_calling_conventions) {
|
||||||
|
define_fff_globals
|
||||||
|
# Create fake generators for 0..MAX_ARGS
|
||||||
|
num_fake_generators = $MAX_ARGS + 1
|
||||||
|
num_fake_generators.times {|arg_count| output_macro(arg_count, false, has_calling_conventions, false)}
|
||||||
|
num_fake_generators.times {|arg_count| output_macro(arg_count, false, has_calling_conventions, true)}
|
||||||
|
# generate the varargs variants
|
||||||
|
(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)}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
gtest/CMakeLists.txt
Normal file
12
gtest/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS " -static")
|
||||||
|
|
||||||
|
# Create the gtest library
|
||||||
|
add_library(gtest src/gtest-all.cc src/gtest-main.cc)
|
||||||
|
target_include_directories(gtest PUBLIC include include/gtest)
|
||||||
|
target_link_libraries(gtest PRIVATE Threads::Threads -static-libstdc++)
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
|
|
||||||
BUILD_DIR = ../build
|
|
||||||
LIBS := -lpthread
|
|
||||||
CPP_OBJS=$(BUILD_DIR)/gtest-all.o $(BUILD_DIR)/gtest-main.o
|
|
||||||
|
|
||||||
SOURCES=gtest-all.cc gtest-main.cc
|
|
||||||
|
|
||||||
|
|
||||||
# Each subdirectory must supply rules for building sources it contributes
|
|
||||||
$(BUILD_DIR)/%.o: %.cc
|
|
||||||
@echo 'Building file: $<'
|
|
||||||
@echo 'Invoking: GCC C++ Compiler'
|
|
||||||
g++ -I../ -O0 -g3 -Wall -DGTEST_USE_OWN_TR1_TUPLE=1 -c -o "$@" "$<"
|
|
||||||
@echo 'Finished building: $<'
|
|
||||||
@echo ' '
|
|
||||||
|
|
||||||
all: $(CPP_OBJS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
-$(RM) $(CPP_OBJS)
|
|
||||||
-@echo ' '
|
|
||||||
|
|
||||||
@@ -1554,11 +1554,29 @@ inline bool operator!=(const GTEST_10_TUPLE_(T)& t,
|
|||||||
// <tr1/functional>. Hence the following #define is a hack to prevent
|
// <tr1/functional>. Hence the following #define is a hack to prevent
|
||||||
// <tr1/functional> from being included.
|
// <tr1/functional> from being included.
|
||||||
# define _TR1_FUNCTIONAL 1
|
# define _TR1_FUNCTIONAL 1
|
||||||
# include <tr1/tuple>
|
# include <tuple>
|
||||||
|
namespace std {
|
||||||
|
namespace tr1 {
|
||||||
|
using ::std::get;
|
||||||
|
using ::std::make_tuple;
|
||||||
|
using ::std::tuple;
|
||||||
|
using ::std::tuple_element;
|
||||||
|
using ::std::tuple_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
# undef _TR1_FUNCTIONAL // Allows the user to #include
|
# undef _TR1_FUNCTIONAL // Allows the user to #include
|
||||||
// <tr1/functional> if he chooses to.
|
// <tr1/functional> if he chooses to.
|
||||||
# else
|
# else
|
||||||
# include <tr1/tuple> // NOLINT
|
# include <tuple> // NOLINT
|
||||||
|
namespace std {
|
||||||
|
namespace tr1 {
|
||||||
|
using ::std::get;
|
||||||
|
using ::std::make_tuple;
|
||||||
|
using ::std::tuple;
|
||||||
|
using ::std::tuple_element;
|
||||||
|
using ::std::tuple_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
|
# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
|
||||||
|
|
||||||
# else
|
# else
|
||||||
56
test/CMakeLists.txt
Normal file
56
test/CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# Create a list of common files needed for tests
|
||||||
|
set(
|
||||||
|
COMMON_FILE_LIST
|
||||||
|
c_test_framework.h
|
||||||
|
test_cases.include
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the C test executable
|
||||||
|
add_executable(c_test fff_test_c.c ${COMMON_FILE_LIST})
|
||||||
|
target_link_libraries(c_test PRIVATE fff)
|
||||||
|
|
||||||
|
# Create the C++ test executable
|
||||||
|
add_executable(cpp_test fff_test_cpp.cpp ${COMMON_FILE_LIST})
|
||||||
|
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
|
||||||
|
${COMMON_FILE_LIST}
|
||||||
|
)
|
||||||
|
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
|
||||||
|
${COMMON_FILE_LIST}
|
||||||
|
)
|
||||||
|
target_link_libraries(cpp_global_test PRIVATE gtest fff)
|
||||||
|
|
||||||
|
# Add the tests for ctest
|
||||||
|
add_test(
|
||||||
|
NAME c_test
|
||||||
|
COMMAND $<TARGET_FILE:c_test>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME cpp_test
|
||||||
|
COMMAND $<TARGET_FILE:cpp_test>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME c_global_test
|
||||||
|
COMMAND $<TARGET_FILE:c_global_test>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME cpp_global_test
|
||||||
|
COMMAND $<TARGET_FILE:cpp_global_test>
|
||||||
|
)
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
|
|
||||||
BUILD_DIR = ../build
|
|
||||||
|
|
||||||
FFF_TEST_CPP_OBJS += \
|
|
||||||
$(BUILD_DIR)/fff_test_cpp.o \
|
|
||||||
$(BUILD_DIR)/gtest-all.o \
|
|
||||||
$(BUILD_DIR)/gtest-main.o
|
|
||||||
|
|
||||||
FFF_TEST_GLOBAL_CPP_OBJS += \
|
|
||||||
$(BUILD_DIR)/fff_test_global_cpp.o \
|
|
||||||
$(BUILD_DIR)/global_fakes.o \
|
|
||||||
$(BUILD_DIR)/gtest-all.o \
|
|
||||||
$(BUILD_DIR)/gtest-main.o
|
|
||||||
|
|
||||||
FFF_TEST_C_OBJS = $(BUILD_DIR)/fff_test_c.o
|
|
||||||
|
|
||||||
FFF_TEST_GLOBAL_C_OBJS += \
|
|
||||||
$(BUILD_DIR)/global_fakes.o \
|
|
||||||
$(BUILD_DIR)/fff_test_global_c.o
|
|
||||||
|
|
||||||
FFF_TEST_CPP_TARGET = $(BUILD_DIR)/fff_test_cpp
|
|
||||||
FFF_TEST_C_TARGET = $(BUILD_DIR)/fff_test_c
|
|
||||||
FFF_TEST_GLOBAL_C_TARGET = $(BUILD_DIR)/fff_test_glob_c
|
|
||||||
FFF_TEST_GLOBAL_CPP_TARGET = $(BUILD_DIR)/fff_test_glob_cpp
|
|
||||||
|
|
||||||
LIBS := -lpthread
|
|
||||||
# All Target
|
|
||||||
all: $(FFF_TEST_CPP_TARGET) $(FFF_TEST_C_TARGET) $(FFF_TEST_GLOBAL_C_TARGET) $(FFF_TEST_GLOBAL_CPP_TARGET)
|
|
||||||
|
|
||||||
|
|
||||||
# Each subdirectory must supply rules for building sources it contributes
|
|
||||||
$(BUILD_DIR)/%.o: %.cpp
|
|
||||||
@echo 'Building file: $<'
|
|
||||||
@echo 'Invoking: GCC C++ Compiler'
|
|
||||||
g++ -I../ -O0 -g3 -Wall -DGTEST_USE_OWN_TR1_TUPLE=1 -c -o "$@" "$<"
|
|
||||||
@echo 'Finished building: $<'
|
|
||||||
@echo ' '
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.c
|
|
||||||
@echo 'Building file: $<'
|
|
||||||
@echo 'Invoking: GCC C Compiler'
|
|
||||||
gcc -I../ -O0 -g3 -Wall -std=c99 -c -o "$@" "$<"
|
|
||||||
@echo 'Finished building: $<'
|
|
||||||
@echo ' '
|
|
||||||
|
|
||||||
|
|
||||||
# Link targets
|
|
||||||
$(FFF_TEST_CPP_TARGET): $(FFF_TEST_CPP_OBJS)
|
|
||||||
@echo 'Building target: $@'
|
|
||||||
@echo 'Invoking: GCC C++ Linker'
|
|
||||||
g++ -o "$(FFF_TEST_CPP_TARGET)" $(FFF_TEST_CPP_OBJS) $(LIBS)
|
|
||||||
@echo 'Finished building target: $@'
|
|
||||||
@echo ' '
|
|
||||||
|
|
||||||
$(FFF_TEST_C_TARGET): $(FFF_TEST_C_OBJS)
|
|
||||||
@echo 'Building target: $@'
|
|
||||||
@echo 'Invoking: GCC C Linker'
|
|
||||||
gcc -o "$(FFF_TEST_C_TARGET)" $(FFF_TEST_C_OBJS) $(LIBS)
|
|
||||||
@echo 'Finished building target: $@'
|
|
||||||
@echo ' '
|
|
||||||
|
|
||||||
$(FFF_TEST_GLOBAL_C_TARGET): $(FFF_TEST_GLOBAL_C_OBJS)
|
|
||||||
@echo 'Building target: $@'
|
|
||||||
@echo 'Invoking: GCC C++ Linker'
|
|
||||||
gcc -o "$(FFF_TEST_GLOBAL_C_TARGET)" $(FFF_TEST_GLOBAL_C_OBJS) $(LIBS) $(WRAP_LDFLAGS)
|
|
||||||
@echo 'Finished building target: $@'
|
|
||||||
@echo ' '
|
|
||||||
|
|
||||||
$(FFF_TEST_GLOBAL_CPP_TARGET): $(FFF_TEST_GLOBAL_CPP_OBJS)
|
|
||||||
@echo 'Building target: $@'
|
|
||||||
@echo 'Invoking: GCC C++ Linker'
|
|
||||||
g++ -o "$(FFF_TEST_GLOBAL_CPP_TARGET)" $(FFF_TEST_GLOBAL_CPP_OBJS) $(LIBS)
|
|
||||||
@echo 'Finished building target: $@'
|
|
||||||
@echo ' '
|
|
||||||
|
|
||||||
# Other Targets
|
|
||||||
clean:
|
|
||||||
-$(RM) $(FFF_TEST_CPP_OBJS) $(FFF_TEST_GLOBAL_C_OBJS) $(FFF_TEST_C_OBJS) \
|
|
||||||
$(FFF_TEST_CPP_TARGET) $(FFF_TEST_C_TARGET) $(FFF_TEST_GLOBAL_CPP_TARGET) $(FFF_TEST_GLOBAL_C_TARGET)
|
|
||||||
-@echo ' '
|
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#define OVERRIDE_CALL_HIST_LEN 17u
|
#define OVERRIDE_CALL_HIST_LEN 17u
|
||||||
#define FFF_CALL_HISTORY_LEN OVERRIDE_CALL_HIST_LEN
|
#define FFF_CALL_HISTORY_LEN OVERRIDE_CALL_HIST_LEN
|
||||||
|
|
||||||
#include "../fff.h"
|
#include "fff.h"
|
||||||
#include "c_test_framework.h"
|
#include "c_test_framework.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -21,18 +21,31 @@ struct MyStruct {
|
|||||||
int y;
|
int y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef TEST_WITH_CALLING_CONVENTIONS
|
||||||
FAKE_VOID_FUNC(voidfunc1, int);
|
FAKE_VOID_FUNC(voidfunc1, int);
|
||||||
FAKE_VOID_FUNC(voidfunc2, char, char);
|
FAKE_VOID_FUNC(voidfunc2, char, char);
|
||||||
FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
||||||
FAKE_VALUE_FUNC(long, longfunc0);
|
FAKE_VALUE_FUNC(long, longfunc0);
|
||||||
FAKE_VALUE_FUNC(enum MYBOOL, enumfunc0);
|
FAKE_VALUE_FUNC(enum MYBOOL, enumfunc0);
|
||||||
FAKE_VALUE_FUNC(struct MyStruct, structfunc0);
|
FAKE_VALUE_FUNC(struct MyStruct, structfunc0);
|
||||||
FAKE_VOID_FUNC_VARARG(voidfunc3var, char *, int, ...);
|
FAKE_VOID_FUNC_VARARG(voidfunc3var, const char *, int, ...);
|
||||||
FAKE_VALUE_FUNC_VARARG(int, valuefunc3var, 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_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_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);
|
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
|
||||||
|
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_VALUE_FUNC(enum MYBOOL, __cdecl, enumfunc0);
|
||||||
|
FAKE_VALUE_FUNC(struct MyStruct, __cdecl, structfunc0);
|
||||||
|
FAKE_VOID_FUNC_VARARG(__cdecl, voidfunc3var, char *, int, ...);
|
||||||
|
FAKE_VALUE_FUNC_VARARG(int, __cdecl, valuefunc3var, char *, int, ...);
|
||||||
|
FAKE_VALUE_FUNC(int, __cdecl, strlcpy3, char* const, const char* const, const size_t);
|
||||||
|
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
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
@@ -103,6 +116,10 @@ int main()
|
|||||||
|
|
||||||
RUN_TEST(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments);
|
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, 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, can_capture_upto_20_arguments_correctly);
|
||||||
RUN_TEST(FFFTestSuite, value_func_can_capture_upto_20_arguments_correctly);
|
RUN_TEST(FFFTestSuite, value_func_can_capture_upto_20_arguments_correctly);
|
||||||
|
|||||||
@@ -12,17 +12,30 @@
|
|||||||
#define OVERRIDE_CALL_HIST_LEN 17u
|
#define OVERRIDE_CALL_HIST_LEN 17u
|
||||||
#define FFF_CALL_HISTORY_LEN OVERRIDE_CALL_HIST_LEN
|
#define FFF_CALL_HISTORY_LEN OVERRIDE_CALL_HIST_LEN
|
||||||
|
|
||||||
#include "../fff.h"
|
#include "fff.h"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
DEFINE_FFF_GLOBALS
|
DEFINE_FFF_GLOBALS
|
||||||
|
|
||||||
|
#ifndef TEST_WITH_CALLING_CONVENTIONS
|
||||||
FAKE_VOID_FUNC(voidfunc1, int);
|
FAKE_VOID_FUNC(voidfunc1, int);
|
||||||
FAKE_VOID_FUNC(voidfunc2, char, char);
|
FAKE_VOID_FUNC(voidfunc2, char, char);
|
||||||
FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
||||||
FAKE_VALUE_FUNC(long, longfunc0);
|
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_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);
|
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
|
||||||
|
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
|
||||||
|
|
||||||
class FFFTestSuite: public testing::Test
|
class FFFTestSuite: public testing::Test
|
||||||
{
|
{
|
||||||
@@ -33,12 +46,12 @@ public:
|
|||||||
RESET_FAKE(voidfunc2);
|
RESET_FAKE(voidfunc2);
|
||||||
RESET_FAKE(longfunc0);
|
RESET_FAKE(longfunc0);
|
||||||
RESET_FAKE(voidfunc1outparam);
|
RESET_FAKE(voidfunc1outparam);
|
||||||
|
RESET_FAKE(voidfunc3var);
|
||||||
|
RESET_FAKE(valuefunc3var);
|
||||||
FFF_RESET_HISTORY();
|
FFF_RESET_HISTORY();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "test_cases.include"
|
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, default_constants_can_be_overridden)
|
TEST_F(FFFTestSuite, default_constants_can_be_overridden)
|
||||||
{
|
{
|
||||||
unsigned sizeCallHistory = (sizeof fff.call_history) / (sizeof fff.call_history[0]);
|
unsigned sizeCallHistory = (sizeof fff.call_history) / (sizeof fff.call_history[0]);
|
||||||
@@ -46,3 +59,43 @@ TEST_F(FFFTestSuite, default_constants_can_be_overridden)
|
|||||||
ASSERT_EQ(OVERRIDE_ARG_HIST_LEN, voidfunc2_fake.arg_history_len);
|
ASSERT_EQ(OVERRIDE_ARG_HIST_LEN, voidfunc2_fake.arg_history_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fake declared in C++ context (not extern "C", using namespace)
|
||||||
|
// before the fake is declared
|
||||||
|
namespace cxx
|
||||||
|
{
|
||||||
|
typedef int int_t;
|
||||||
|
void voidfunc1(cxx::int_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now declare the fake. Must be in the same namespace as the
|
||||||
|
// original declaration.
|
||||||
|
namespace cxx
|
||||||
|
{
|
||||||
|
#ifndef TEST_WITH_CALLING_CONVENTIONS
|
||||||
|
FAKE_VOID_FUNC(voidfunc1, cxx::int_t);
|
||||||
|
#else
|
||||||
|
FAKE_VOID_FUNC(_cdecl, voidfunc1, cxx::int_t);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FFFTestSuite, cxx_fake_is_called)
|
||||||
|
{
|
||||||
|
cxx::voidfunc1(66);
|
||||||
|
ASSERT_EQ(cxx::voidfunc1_fake.call_count, 1u);
|
||||||
|
ASSERT_EQ(cxx::voidfunc1_fake.arg0_val, 66);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cxx_my_custom_fake_called = 0;
|
||||||
|
void cxx_my_custom_fake(cxx::int_t i)
|
||||||
|
{
|
||||||
|
cxx_my_custom_fake_called++;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FFFTestSuite, cxx_can_register_custom_fake)
|
||||||
|
{
|
||||||
|
cxx::voidfunc1_fake.custom_fake = cxx_my_custom_fake;
|
||||||
|
cxx::voidfunc1(66);
|
||||||
|
ASSERT_EQ(1, cxx_my_custom_fake_called);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "test_cases.include"
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ int main()
|
|||||||
|
|
||||||
RUN_TEST(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments);
|
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, 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, can_capture_upto_20_arguments_correctly);
|
||||||
RUN_TEST(FFFTestSuite, value_func_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(voidfunc2);
|
||||||
RESET_FAKE(longfunc0);
|
RESET_FAKE(longfunc0);
|
||||||
RESET_FAKE(voidfunc1outparam);
|
RESET_FAKE(voidfunc1outparam);
|
||||||
|
RESET_FAKE(voidfunc3var);
|
||||||
|
RESET_FAKE(valuefunc3var);
|
||||||
FFF_RESET_HISTORY();
|
FFF_RESET_HISTORY();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "global_fakes.h"
|
#include "global_fakes.h"
|
||||||
#include <string.h> // for memcpy
|
#include <string.h> // for memcpy
|
||||||
|
|
||||||
|
#ifndef TEST_WITH_CALLING_CONVENTIONS
|
||||||
DEFINE_FAKE_VOID_FUNC(voidfunc1, int);
|
DEFINE_FAKE_VOID_FUNC(voidfunc1, int);
|
||||||
DEFINE_FAKE_VOID_FUNC(voidfunc2, char, char);
|
DEFINE_FAKE_VOID_FUNC(voidfunc2, char, char);
|
||||||
DEFINE_FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
DEFINE_FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
||||||
@@ -15,3 +16,19 @@ DEFINE_FAKE_VALUE_FUNC(int, strlcpy3, char* const, const char* const, const size
|
|||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
DEFINE_FAKE_VOID_FUNC(voidfunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
DEFINE_FAKE_VOID_FUNC(voidfunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||||
DEFINE_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);
|
DEFINE_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
|
||||||
|
DEFINE_FAKE_VOID_FUNC(__cdecl, voidfunc1, int);
|
||||||
|
DEFINE_FAKE_VOID_FUNC(__cdecl, voidfunc2, char, char);
|
||||||
|
DEFINE_FAKE_VOID_FUNC(__cdecl, voidfunc1outparam, char *);
|
||||||
|
|
||||||
|
DEFINE_FAKE_VALUE_FUNC(long, __cdecl, longfunc0);
|
||||||
|
DEFINE_FAKE_VALUE_FUNC(enum MYBOOL, __cdecl, enumfunc0);
|
||||||
|
DEFINE_FAKE_VALUE_FUNC(struct MyStruct, __cdecl, structfunc0);
|
||||||
|
DEFINE_FAKE_VOID_FUNC_VARARG(__cdecl, voidfunc3var, const char *, int, ...);
|
||||||
|
DEFINE_FAKE_VALUE_FUNC_VARARG(int, __cdecl, valuefunc3var, const char *, int, ...);
|
||||||
|
#ifndef __cplusplus
|
||||||
|
DEFINE_FAKE_VALUE_FUNC(int, __cdecl, strlcpy3, char* const, const char* const, const size_t);
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
DEFINE_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);
|
||||||
|
DEFINE_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
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
#ifndef GLOBAL_FAKES_H_
|
#ifndef GLOBAL_FAKES_H_
|
||||||
#define GLOBAL_FAKES_H_
|
#define GLOBAL_FAKES_H_
|
||||||
|
|
||||||
#include "../fff.h"
|
#include "fff.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +15,7 @@ enum MYBOOL enumfunc();
|
|||||||
struct MyStruct structfunc();
|
struct MyStruct structfunc();
|
||||||
//// End Imaginary production code header file ///
|
//// End Imaginary production code header file ///
|
||||||
|
|
||||||
|
#ifndef TEST_WITH_CALLING_CONVENTIONS
|
||||||
DECLARE_FAKE_VOID_FUNC(voidfunc1, int);
|
DECLARE_FAKE_VOID_FUNC(voidfunc1, int);
|
||||||
DECLARE_FAKE_VOID_FUNC(voidfunc2, char, char);
|
DECLARE_FAKE_VOID_FUNC(voidfunc2, char, char);
|
||||||
DECLARE_FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
DECLARE_FAKE_VOID_FUNC(voidfunc1outparam, char *);
|
||||||
@@ -26,8 +26,24 @@ DECLARE_FAKE_VOID_FUNC_VARARG(voidfunc3var, const char *, int, ...);
|
|||||||
DECLARE_FAKE_VALUE_FUNC_VARARG(int, valuefunc3var, const char *, int, ...);
|
DECLARE_FAKE_VALUE_FUNC_VARARG(int, valuefunc3var, const char *, int, ...);
|
||||||
DECLARE_FAKE_VOID_FUNC(voidfunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
DECLARE_FAKE_VOID_FUNC(voidfunc20, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
|
||||||
DECLARE_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);
|
DECLARE_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
|
||||||
|
DECLARE_FAKE_VOID_FUNC(__cdecl, voidfunc1, int);
|
||||||
|
DECLARE_FAKE_VOID_FUNC(__cdecl, voidfunc2, char, char);
|
||||||
|
DECLARE_FAKE_VOID_FUNC(__cdecl, voidfunc1outparam, char *);
|
||||||
|
DECLARE_FAKE_VALUE_FUNC(long, __cdecl, longfunc0);
|
||||||
|
DECLARE_FAKE_VALUE_FUNC(enum MYBOOL, __cdecl, enumfunc0);
|
||||||
|
DECLARE_FAKE_VALUE_FUNC(struct MyStruct, __cdecl, structfunc0);
|
||||||
|
DECLARE_FAKE_VOID_FUNC_VARARG(__cdecl, voidfunc3var, const char *, int, ...);
|
||||||
|
DECLARE_FAKE_VALUE_FUNC_VARARG(int, __cdecl, valuefunc3var, const char *, int, ...);
|
||||||
|
DECLARE_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);
|
||||||
|
DECLARE_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
|
||||||
|
|
||||||
#ifndef __cplusplus
|
#ifndef __cplusplus
|
||||||
|
#ifndef TEST_WITH_CALLING_CONVENTIONS
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, strlcpy3, char* const, const char* const, const size_t);
|
DECLARE_FAKE_VALUE_FUNC(int, strlcpy3, char* const, const char* const, const size_t);
|
||||||
|
#else
|
||||||
|
DECLARE_FAKE_VALUE_FUNC(int, __cdecl, strlcpy3, char* const, const char* const, const size_t);
|
||||||
|
#endif
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
#endif /* GLOBAL_FAKES_H_ */
|
#endif /* GLOBAL_FAKES_H_ */
|
||||||
|
|||||||
@@ -158,11 +158,13 @@ TEST_F(FFFTestSuite, register_call_macro_registers_two_calls)
|
|||||||
TEST_F(FFFTestSuite, reset_call_history_resets_call_history)
|
TEST_F(FFFTestSuite, reset_call_history_resets_call_history)
|
||||||
{
|
{
|
||||||
REGISTER_CALL(longfunc0);
|
REGISTER_CALL(longfunc0);
|
||||||
|
REGISTER_CALL(voidfunc1);
|
||||||
FFF_RESET_HISTORY();
|
FFF_RESET_HISTORY();
|
||||||
REGISTER_CALL(voidfunc2);
|
REGISTER_CALL(voidfunc2);
|
||||||
|
|
||||||
ASSERT_EQ(1u, fff.call_history_idx);
|
ASSERT_EQ(1u, fff.call_history_idx);
|
||||||
ASSERT_EQ(fff.call_history[0], (void *)voidfunc2);
|
ASSERT_EQ(fff.call_history[0], (void *)voidfunc2);
|
||||||
|
ASSERT_EQ(fff.call_history[1], (void *)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, call_history_will_not_write_past_array_bounds)
|
TEST_F(FFFTestSuite, call_history_will_not_write_past_array_bounds)
|
||||||
@@ -284,17 +286,18 @@ TEST_F(FFFTestSuite, return_value_saved_in_history)
|
|||||||
ASSERT_EQ(longfunc0_fake.return_val_history[i], i + 1);
|
ASSERT_EQ(longfunc0_fake.return_val_history[i], i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
long custom_longfunc1()
|
|
||||||
|
long custom_longfunc1(void)
|
||||||
{
|
{
|
||||||
return 42;
|
return 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
long custom_longfunc2()
|
long custom_longfunc2(void)
|
||||||
{
|
{
|
||||||
return 15;
|
return 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
long custom_longfunc3()
|
long custom_longfunc3(void)
|
||||||
{
|
{
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
@@ -304,6 +307,7 @@ TEST_F(FFFTestSuite, custom_fake_seq_return_values_saved_in_history)
|
|||||||
long (*custom_fakes[])(void) = {custom_longfunc1,
|
long (*custom_fakes[])(void) = {custom_longfunc1,
|
||||||
custom_longfunc2,
|
custom_longfunc2,
|
||||||
custom_longfunc3};
|
custom_longfunc3};
|
||||||
|
|
||||||
SET_CUSTOM_FAKE_SEQ(longfunc0, custom_fakes, 3);
|
SET_CUSTOM_FAKE_SEQ(longfunc0, custom_fakes, 3);
|
||||||
|
|
||||||
longfunc0();
|
longfunc0();
|
||||||
@@ -343,6 +347,13 @@ long my_custom_value_fake(void)
|
|||||||
{
|
{
|
||||||
return MEANING_OF_LIFE;
|
return MEANING_OF_LIFE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long my_custom_value_fake2(void)
|
||||||
|
{
|
||||||
|
static long val = 0;
|
||||||
|
return val++;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_return_value)
|
TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_return_value)
|
||||||
{
|
{
|
||||||
longfunc0_fake.custom_fake = my_custom_value_fake;
|
longfunc0_fake.custom_fake = my_custom_value_fake;
|
||||||
@@ -350,7 +361,100 @@ TEST_F(FFFTestSuite, when_value_custom_fake_called_THEN_it_returns_custom_return
|
|||||||
ASSERT_EQ(MEANING_OF_LIFE, retval);
|
ASSERT_EQ(MEANING_OF_LIFE, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __cplusplus
|
TEST_F(FFFTestSuite, return_values_from_custom_fake_saved_in_history)
|
||||||
|
{
|
||||||
|
longfunc0_fake.custom_fake = my_custom_value_fake2;
|
||||||
|
longfunc0();
|
||||||
|
longfunc0();
|
||||||
|
longfunc0();
|
||||||
|
|
||||||
|
ASSERT_EQ(0, longfunc0_fake.return_val_history[0]);
|
||||||
|
ASSERT_EQ(1, longfunc0_fake.return_val_history[1]);
|
||||||
|
ASSERT_EQ(2, longfunc0_fake.return_val_history[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
TEST_F(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments)
|
||||||
{
|
{
|
||||||
voidfunc3var("0 parameters", 0);
|
voidfunc3var("0 parameters", 0);
|
||||||
@@ -358,7 +462,7 @@ TEST_F(FFFTestSuite, use_void_vararg_fake_with_different_number_of_arguments)
|
|||||||
voidfunc3var("2 parameters", 2, 10, 20);
|
voidfunc3var("2 parameters", 2, 10, 20);
|
||||||
voidfunc3var("3 parameters", 3, 10, 20, 30);
|
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";
|
char msg[] = "3 parameters";
|
||||||
ASSERT_EQ(strcmp(voidfunc3var_fake.arg0_val, msg), 0);
|
ASSERT_EQ(strcmp(voidfunc3var_fake.arg0_val, msg), 0);
|
||||||
ASSERT_EQ(3, voidfunc3var_fake.arg1_val);
|
ASSERT_EQ(3, voidfunc3var_fake.arg1_val);
|
||||||
@@ -371,12 +475,11 @@ TEST_F(FFFTestSuite, use_value_vararg_fake_with_different_number_of_arguments)
|
|||||||
valuefunc3var("2 parameters", 2, 10, 20);
|
valuefunc3var("2 parameters", 2, 10, 20);
|
||||||
valuefunc3var("3 parameters", 3, 10, 20, 30);
|
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";
|
char msg[] = "3 parameters";
|
||||||
ASSERT_EQ(strcmp(valuefunc3var_fake.arg0_val, msg), 0);
|
ASSERT_EQ(strcmp(valuefunc3var_fake.arg0_val, msg), 0);
|
||||||
ASSERT_EQ(3, valuefunc3var_fake.arg1_val);
|
ASSERT_EQ(3, valuefunc3var_fake.arg1_val);
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
TEST_F(FFFTestSuite, can_capture_upto_20_arguments_correctly)
|
TEST_F(FFFTestSuite, can_capture_upto_20_arguments_correctly)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user