25 Commits

Author SHA1 Message Date
Mark VanderVoord 85ed528e68 Bump version and change log. 2026-07-01 14:41:56 -04:00
Mark VanderVoord 6560abf792 Merge pull request #535 from ThrowTheSwitch/refactor/actions
Implement broader testing across platforms.
2026-07-01 14:14:50 -04:00
Mark VanderVoord a737db33eb Another tweak to regex handling in parser for performance purposes. 2026-07-01 14:09:52 -04:00
Mark VanderVoord 922ebfa699 Whoops. Fixed last issue. 2026-07-01 10:41:29 -04:00
Mark VanderVoord 847e87e71e Fall back to iterative brace removal because I suspect regex version is too slow.
Further tidying of ci.
2026-07-01 10:27:01 -04:00
Mark VanderVoord af43eb87b1 streamline function declaration regex. 2026-07-01 07:48:23 -04:00
Mark VanderVoord 9a412dfccc More tweaking of flow. 2026-07-01 07:14:45 -04:00
Mark VanderVoord bf10a5bc8a Further refine testing process for speed and coverage. 2026-06-30 22:24:17 -04:00
Mark VanderVoord 895d066157 Change the exit code for valgrind so that we can detect issues. 2026-06-30 22:15:29 -04:00
Mark VanderVoord 61f6f2991b Add single instance of a valgrind test to verify memory handling in CMock. 2026-06-30 16:56:16 -04:00
Mark VanderVoord ff87d4c08a Fix some issues that windows noticed with volatile handling.
Improve some docs.
2026-06-30 16:11:43 -04:00
Mark VanderVoord 3d745ce8f5 disable color handling when windows testing. 2026-06-30 15:20:01 -04:00
Mark VanderVoord aedb21638f Implement broader testing across platforms. 2026-06-30 14:53:17 -04:00
Mark VanderVoord 8c3a33617f Add documentation for last feature 2026-06-30 13:36:37 -04:00
Mark VanderVoord 4b523b568e Add ability to monitor mock behaviors across all mocks and expectations (Implements #403) 2026-06-30 13:31:49 -04:00
Mark VanderVoord 125fcdef3c Fix format of latest regex. 2026-06-30 11:28:22 -04:00
Mark VanderVoord 1724a4d0a2 Fixed another type of function pointer getting falsely identified as a function. 2026-06-30 11:17:09 -04:00
Mark VanderVoord a25354f659 Improve alignment determination (Fixes #178) 2026-06-30 10:59:10 -04:00
Mark VanderVoord ebdc8ee128 Improved the way stubs and callbacks deal with call counts (Implements #132) 2026-06-30 10:15:18 -04:00
Mark VanderVoord dd922f17b0 Automatically strip out compile-time assertions so they're not confused with function prototypes (Fixes #128) 2026-06-30 09:50:33 -04:00
Mark VanderVoord b01cfdc531 Improve documentation for custom types (#124) 2026-06-29 17:08:43 -04:00
Mark VanderVoord 38dc454826 Support basic #if 0 and #if 1 handling... not more complicated preprocessing. 2026-06-29 16:48:31 -04:00
Mark VanderVoord 2e76508412 Use correct pointer macro. 2026-06-29 16:10:50 -04:00
Mark VanderVoord a2ac5c2c63 Improve handling of volatile keyword (Fixes #110 and #135) 2026-06-29 16:02:54 -04:00
Mark VanderVoord 3541b31c56 Merge pull request #534 from ThrowTheSwitch/bugfix/interactions
Bugfix/interactions
2026-06-26 15:36:59 -04:00
39 changed files with 1459 additions and 94 deletions
+144 -25
View File
@@ -11,42 +11,161 @@ on:
branches: [ master ] branches: [ master ]
jobs: jobs:
# Job: Unit test suite # Job: Ruby unit tests across all supported Ruby versions and OSes.
unit-tests: # These are fast (no C compilation) and verify the generator logic is Ruby-version-portable.
name: "Unit Tests" ruby-tests:
runs-on: ubuntu-latest name: "Ruby Tests (${{ matrix.os }}, Ruby ${{ matrix.ruby }})"
runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
ruby: ['3.0', '3.1', '3.2', '3.3'] ruby: ['3.0', '3.1', '3.2', '3.3']
exclude:
# create sparse matrix to avoid pointless duplication
- os: macos-latest
ruby: '3.0'
- os: macos-latest
ruby: '3.1'
- os: macos-latest
ruby: '3.2'
- os: windows-latest
ruby: '3.0'
- os: windows-latest
ruby: '3.1'
- os: windows-latest
ruby: '3.2'
steps: steps:
# Install Multilib
- name: Install Multilib
run: |
sudo apt-get update -qq
sudo apt-get install --assume-yes --quiet gcc-multilib
# Checks out repository under $GITHUB_WORKSPACE
- name: Checkout Latest Repo - name: Checkout Latest Repo
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
# Setup Ruby Testing Tools to do tests on multiple ruby version - name: Setup Ruby
- name: Setup Ruby Testing Tools
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: ${{ matrix.ruby }} ruby-version: ${{ matrix.ruby }}
# Install Ruby Testing Tools - name: Install Ruby Dependencies
- name: Setup Ruby Testing Tools
run: | run: |
sudo gem install rspec gem install bundler
sudo gem install rubocop -v 1.57.2 bundle install
sudo gem install bundler
bundle update
bundle install
# Run Tests - name: Run Ruby Unit Tests
- name: Run All Unit Tests env:
NO_COLOR: ${{ matrix.os == 'windows-latest' && '1' || '' }}
run: | run: |
cd test && rake ci cd test && rake test:unit
- name: Run Style Check
env:
NO_COLOR: ${{ matrix.os == 'windows-latest' && '1' || '' }}
run: |
cd test && rake style:check
# Job: C compilation and system tests — only needs to run on one Ruby version per OS,
# since the generated C code and runtime behavior don't vary with the Ruby version.
c-tests:
name: "C Tests (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
# Install Multilib (Linux only)
- name: Install Multilib
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update -qq
sudo apt-get install --assume-yes --quiet gcc-multilib
# Add MinGW GCC to PATH (Windows only — MSYS2 is pre-installed on the runner)
- name: Add GCC to PATH
if: matrix.os == 'windows-latest'
run: echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Checkout Latest Repo
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
- name: Install Ruby Dependencies
run: |
gem install bundler
bundle install
- name: Run C Unit Tests
env:
NO_COLOR: ${{ matrix.os == 'windows-latest' && '1' || '' }}
run: cd test && rake test:c
- name: Run System Tests
env:
NO_COLOR: ${{ matrix.os == 'windows-latest' && '1' || '' }}
run: cd test && rake test:system
- name: Run Examples
env:
NO_COLOR: ${{ matrix.os == 'windows-latest' && '1' || '' }}
run: cd test && rake test:examples
# Job: Valgrind memory-leak check (Linux/gcc_64 only, latest Ruby)
valgrind:
name: "Valgrind Memory Check"
runs-on: ubuntu-latest
steps:
- name: Install Dependencies
run: |
sudo apt-get update -qq
sudo apt-get install --assume-yes --quiet gcc-multilib valgrind
- name: Checkout Latest Repo
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
- name: Install Ruby Dependencies
run: |
gem install bundler
bundle install
# Build and run C unit tests, then re-run the executable under valgrind.
# test:system clobbers the build directory, so check TestCMockC before that happens.
- name: Build and Run C Unit Tests
run: cd test && rake config[gcc_64_valgrind] test:c
- name: Valgrind Check - C Unit Tests
run: |
valgrind --leak-check=full --track-origins=yes --error-exitcode=1 \
test/system/build/TestCMockC.exe
# Build and run system tests, then re-run each executable under valgrind.
- name: Build and Run System Tests
run: cd test && rake config[gcc_64_valgrind] test:system
- name: Valgrind Check - System Tests
run: |
failed=0
for exe in test/system/build/test_*.exe; do
echo "Checking: $exe"
# Use exit code 42 to distinguish valgrind errors from Unity test failures.
# Some executables intentionally contain tests expected to fail (testing CMock's
# error-handling), so Unity exits non-zero. The `|| exit_code=$?` prevents bash's
# set -e from aborting the script on Unity's non-zero exit, while still capturing
# valgrind's own error exit code (42) separately.
exit_code=0
valgrind --leak-check=full --track-origins=yes --error-exitcode=42 "$exe" || exit_code=$?
[ $exit_code -eq 42 ] && failed=1
done
exit $failed
+4 -1
View File
@@ -1 +1,4 @@
source "http://rubygems.org/" source "https://rubygems.org/"
gem 'rspec'
gem 'rubocop', '1.57.2'
+35
View File
@@ -13,6 +13,41 @@ Prior to 2008, the project was an internal project and not released to the publi
## Log ## Log
### CMock 2.7.0 (July 2026)
New Features:
- Significant improvements to array and pointer handling:
- Arrays are now passed as arrays. Yes, even multidimensional ones. (Fixes #69, #119, #213, #422)
- Array plugin now supports comparing `char*` (string) arguments as byte arrays via `_ExpectWithArray`, while still treating them as strings via `_Expect` (Fixes #262 and #177)
- Improved automatic detection of pointer/length argument pairs (Fixes #520)
- When a pointer is auto-paired with a size argument, `_ExpectWithArrayExtended` is also generated as a fallback to allow explicit depth override (Fixes #476)
- `void*` arguments now default to pointer comparison without the array plugin, and to byte-by-byte comparison when the array plugin is active (Fixes #400)
- Handles *simple* preprocessor features like #if 1 and #if 0 (Fixes #163)
- Ignores static assertions of various types (Fixes #128)
- Added option to trace all mock setup and mocked calls (Implements #403)
- Significant improvements to header parsing speed
Significant Bugfixes:
- Fixed matching of pointer/len argument pairs in reverse order (Fixes #479)
- Fixed handling of array of pointers or a pointer to an array (Fixes #450)
- Fixed const and pointer order handling issues (Fixes #484 and #485)
- Fixed handling of skeleton paths (Fixes #488)
- Fixed handling of memory alignment issues (Fixes #178)
- Fixed handling of failures in teardown (#67)
- Improved handling of function-looking structs (Fixes #513 and #334)
- Improved handling of function-looking macros (Fixes #502)
- Improved handling of volatiles (Fixes #110 and #135)
- Improved handling of stub and callback counters (Fixes #132)
- Improved handling and testing of Windows (Fixes #435)
Other:
- Added verification that memory errors are reported and stop tests (Verifies #463)
- Added verification that CMock features pass Valgrind (Verifies #506)
- Documented custom type support (#124)
### CMock 2.6.0 (January 2025) ### CMock 2.6.0 (January 2025)
New Features: New Features:
-1
View File
@@ -9,5 +9,4 @@ be found on our Github repository.
## Issues ## Issues
- Able to parse most C header files without preprocessor needs, but when handling headers with significant preprocessor usage (#ifdefs, etc), it can get confused - Able to parse most C header files without preprocessor needs, but when handling headers with significant preprocessor usage (#ifdefs, etc), it can get confused
- Multi-dimensional arrays currently simplified to single dimensional arrays of the full size
- Incomplete support for VarArgs - Incomplete support for VarArgs
+180 -25
View File
@@ -1,16 +1,32 @@
CMock: Argument Validation CMock: Argument Validation
========================== ==========================
Much of the power of CMock comes from its ability to automatically Much of the power of CMock comes from its ability to automatically
validate that the arguments passed to mocked functions are the validate that the arguments passed to mocked functions are the
values that were expected to be passed. CMock puts a lot of effort values that were expected to be passed. CMock puts a lot of effort
into guessing how the user would most like to see those values into guessing how the user would most like to see those values
compared, and then represented when failures are encountered. compared, and then represented when failures are encountered.
Like Unity, CMock follows a philosophy of making its best guesses, Like Unity, CMock follows a philosophy of making its best guesses,
and then allowing the user to explicity specify any features that and then allowing the user to explicitly specify any features that
they would like to change or customize. they would like to change or customize.
Quick Reference: Which Option Should I Use?
-------------------------------------------
| Situation | Recommended Option |
|-----------|-------------------|
| Built-in C types (`int`, `uint8_t`, `float`, …) | Nothing — Option 1 handles these automatically |
| Simple typedef or `#define` alias of a known type | Option 2: add a `:treat_as` entry |
| Small `enum` type | Option 2: map to `INT8`, `INT16`, or `INT` |
| Opaque handle / function pointer where only identity matters | Option 2: map to `PTR` |
| `typedef`'d fixed-size array | Option 2: `:treat_as_array` |
| Legacy `typedef void MY_VOID` | Option 2: `:treat_as_void` |
| `struct` or `union` needing field-level comparison | Option 3: custom assertion + `:unity_helper_path` |
| Pointer to a `struct` with a custom assertion | Option 2 + 3: custom assertion, then `:treat_as` the pointer |
| Type that changes meaning per test, or one-off complex logic | Option 4: Callback |
| Last resort — type is unknown and rough equality is enough | Option 1b: memcmp fallback (automatic) |
Option 1: Common Types Option 1: Common Types
---------------------- ----------------------
@@ -72,22 +88,129 @@ a custom type?
Option 2: Treat-As Option 2: Treat-As
------------------ ------------------
CMock maintains a list of non-standard types which are basically CMock maintains a list of non-standard types which are basically
aliases of standard types. For example, a common shorthand for aliases of standard types. For example, a common shorthand for
a single-byte unsigned integer might be `u8` or `U8` or `UNIT8`. a single-byte unsigned integer might be `u8` or `U8` or `UINT8`.
Any of these can simply be mapped to the standard Any of these can simply be mapped to the standard
`TEST_ASSERT_EQUAL_HEX8`. `TEST_ASSERT_EQUAL_HEX8`.
While CMock has its own list of `:treat_as` mappings, you can ### Default Handlers
CMock ships with a built-in `:treat_as` list that already covers the
most common type aliases found in embedded C codebases. You get all
of these for free without any configuration:
| C Type | Unity Assertion Used |
|---------------------|---------------------------|
| `int` | `INT` |
| `char` | `INT8` |
| `short` | `INT16` |
| `long` | `INT` |
| `unsigned int` | `HEX32` |
| `unsigned long` | `HEX32` |
| `unsigned short` | `HEX16` |
| `unsigned char` | `HEX8` |
| `int8_t` / `INT8_T` / `int8` | `INT8` |
| `int16_t` / `INT16_T` / `int16` | `INT16` |
| `int32_t` / `INT32_T` / `int32` | `INT` |
| `uint8_t` / `UINT8_T` / `uint8` / `UINT8` | `HEX8` |
| `uint16_t` / `UINT16_T` / `uint16` / `UINT16` | `HEX16` |
| `uint32_t` / `UINT32_T` / `uint32` / `UINT32` | `HEX32` |
| `bool` / `bool_t` / `BOOL` / `BOOL_T` | `INT` |
| `char*` | `STRING` |
| `pCHAR` / `cstring` / `CSTRING` | `STRING` |
| `void*` | `HEX8_ARRAY` |
| `float` / `double` | `FLOAT` |
The right-hand side of each mapping is the suffix of the Unity assertion
that will be used. `HEX8` means CMock will call `TEST_ASSERT_EQUAL_HEX8`,
for instance. Pointer variants (ending in `*`) map to the corresponding
array assertion (e.g. `HEX8*``TEST_ASSERT_EQUAL_HEX8_ARRAY`).
### Adding Your Own Mappings
While CMock has its own list of `:treat_as` mappings, you can
add your own pairings to this list. This works especially well for add your own pairings to this list. This works especially well for
the following types: the following types:
- aliases of standard types using `#define` or `typedef` - aliases of standard types using `#define` or `typedef`
- `enum` types (works well as `INT8` or whatever size your enums are) - `enum` types (works well as `INT8` or whatever size your enums are)
- function pointers often work well as `PTR` comparisons - function pointers often work well as `PTR` comparisons
- `union` types sometimes make sense to treat as the largest type... - `union` types sometimes make sense to treat as the largest type...
but this is a judgement call but this is a judgement call
Your entries **merge** with the defaults — you are only adding or
overriding specific types, not replacing the entire list. To remove
a default mapping, set its value to `nil`.
Here is a YAML configuration example:
```yaml
:cmock:
:treat_as:
MY_BOOL: INT # typedef bool MY_BOOL → compare as int
MY_U8: HEX8 # typedef uint8_t MY_U8 → compare as hex byte
MY_U16: HEX16
MY_U32: HEX32
STATUS_T: INT8 # small enum → compare as signed byte
HANDLE_T: PTR # opaque pointer → compare pointer addresses
float: nil # remove the default float mapping (unusual)
```
Or from Ruby:
```ruby
CMock.new(
treat_as: {
'MY_BOOL' => 'INT',
'STATUS_T' => 'INT8',
'HANDLE_T' => 'PTR',
}
).setup_mocks('my_module.h')
```
### Pointer Types in :treat_as
You can map pointer-to-custom-type the same way. Use a `*` suffix on
the right-hand side to indicate the comparison should use the array
variant of the assertion:
```yaml
:treat_as:
MY_DATA_PTR: HEX8* # compares the bytes pointed to, not the address
```
### Related Options: :treat_as_array and :treat_as_void
Two narrower variants of `:treat_as` handle specific edge cases:
**`:treat_as_array`** — for types that are themselves `typedef`'d arrays,
such as `typedef int TenIntegers[10];`. This is a hash of typedef name
to element type:
```yaml
:cmock:
:treat_as_array:
TenIntegers: int
MyBuffer: uint8_t
```
This lets CMock treat parameters of these types the same way it would
treat a pointer-plus-count, enabling features like `ExpectWithArray`
and `ReturnArrayThruPtr`.
**`:treat_as_void`** — for legacy codebases that typedef `void` to a
custom name (e.g. `typedef void MY_VOID;`). Add such names here so
CMock knows functions returning or accepting that type are effectively
`void`:
```yaml
:cmock:
:treat_as_void:
- MY_VOID
- NORETURN_T
```
Option 3: Custom Assertions for Custom Types Option 3: Custom Assertions for Custom Types
-------------------------------------------- --------------------------------------------
@@ -206,31 +329,63 @@ void AssertEqualMyType(const MyType expected, const MyType actual, UNITY_LINE_TY
### Wrapping our Assertion in Macros ### Wrapping our Assertion in Macros
Once you have a function which does the main work, we *need* to create Once you have a function which does the main work, we need to create
one macro, and there are a number of other macros which are useful to macros around it so that the assertion can be used conveniently both
create, in order to treat our assertion just like any other Unity by CMock and directly in test code.
assertion.
`#define UNITY_TEST_ASSERT_EQUAL_MyType(e,a,l,m) AssertEqualMyType(e,a,l,m)` The macro that CMock **requires** is the `UNITY_TEST_ASSERT_EQUAL_` form.
It starts with exactly that prefix, followed by the type name exactly as
declared, and takes four arguments:
The macro above is the one that CMock is looking for. Notice that it ```c
starts with `UNITY_TEST_ASSERT_EQUAL_` followed by the name of our type, #define UNITY_TEST_ASSERT_EQUAL_MyType(e,a,l,m) AssertEqualMyType(e,a,l,m)
*exactly* the way our type is named. The arguments are, in order: ```
- `e` - expected value - `e` - expected value
- `a` - actual value - `a` - actual value
- `l` - line number to report - `l` - line number to report (filled in automatically by CMock)
- `m` - message to append at the end - `m` - message to append at the end
If CMock finds a macro that matches this argument list and naming convention, CMock scans the helper header for macros matching this pattern and
then it can automatically use this assertion where needed... all we need to automatically uses them when it encounters the corresponding type.
do now is tell CMock where to find our custom assertion.
It is also useful (though optional) to add the simpler `TEST_ASSERT_EQUAL_`
form so the assertion is easy to call directly inside your own test
functions:
```c
#define TEST_ASSERT_EQUAL_MyType(e,a) \
UNITY_TEST_ASSERT_EQUAL_MyType(e,a,__LINE__,NULL)
#define TEST_ASSERT_EQUAL_MyType_MESSAGE(e,a,m) \
UNITY_TEST_ASSERT_EQUAL_MyType(e,a,__LINE__,m)
```
With these in place, you can write `TEST_ASSERT_EQUAL_MyType(expected, actual)`
in your tests just like any built-in Unity assertion.
### Informing CMock about our Assertion ### Informing CMock about our Assertion
In the CMock configuration file, in the `:cmock` or `:unity` sections, CMock needs to know which header file(s) contain your custom assertions.
there can be an option for `unity_helper_path`. Add the location of your Set the `:unity_helper_path` option in your CMock configuration to point
new Unity helper file (file with this assertion) to this list. at the helper header:
```yaml
:cmock:
:unity_helper_path:
- test/support/my_types_helper.h
```
Or from Ruby:
```ruby
CMock.new(unity_helper_path: ['test/support/my_types_helper.h'])
.setup_mocks('my_module.h')
```
CMock parses each listed file, finds every `UNITY_TEST_ASSERT_EQUAL_*`
macro definition, and uses those macros automatically when it generates
mocks for parameters or return values of the matching types.
Done! Done!
+45 -3
View File
@@ -189,7 +189,7 @@ care how many times it was called, right?
StopIgnore: StopIgnore:
------- -------
Maybe you want to ignore a particular function for part of a test but dont want to Maybe you want to ignore a particular function for part of a test but don't want to
ignore it later on. In that case, you want to use StopIgnore which will cancel the ignore it later on. In that case, you want to use StopIgnore which will cancel the
previously called Ignore or IgnoreAndReturn requiring you to Expect or otherwise previously called Ignore or IgnoreAndReturn requiring you to Expect or otherwise
handle the call to a function. handle the call to a function.
@@ -199,6 +199,25 @@ handle the call to a function.
* `retval func(void)` => `void func_StopIgnore(void)` * `retval func(void)` => `void func_StopIgnore(void)`
* `retval func(params)` => `void func_StopIgnore(void)` * `retval func(params)` => `void func_StopIgnore(void)`
It's important to note that the effect of this function is immediate and applies to
this function's stack of expectations. So the following will work as intended:
```
Blah_Ignore();
funcThatMightCallBlahButWeDoNotCare();
Blah_StopIgnore();
funcThatWeWantToMakeSureDoesNotCallBlah();
```
But this is NOT going to work, because StopIgnore immediately cancels ignore:
```
Blah_Ignore();
Blah_StopIgnore();
funcThatMightCallBlahButWeDoNotCare();
funcThatWeWantToMakeSureDoesNotCallBlah();
```
IgnoreStateless: IgnoreStateless:
---------------- ----------------
@@ -294,6 +313,14 @@ order (based on any Expects you've set up) before calling the callback.
to the callback instead. In this case, you are replacing the normal mock calls to the callback instead. In this case, you are replacing the normal mock calls
with your own custom stub function. with your own custom stub function.
Calling either `func_AddCallback` or `func_Stub` resets the call count for
that function to zero, so `NumCalls` always reflects calls made since the
current stub or callback was installed.
You can read the current call count at any time using `func_CallCount()`.
This is useful when you need to verify how many times a stub was invoked
without setting up formal Expects.
There is also an older name, `func_StubWithCallback`, which is just an alias There is also an older name, `func_StubWithCallback`, which is just an alias
for either `func_AddCallback` or `func_Stub` depending on setting of the for either `func_AddCallback` or `func_Stub` depending on setting of the
`:callback_after_arg_check` toggle. This is deprecated and we recommend using `:callback_after_arg_check` toggle. This is deprecated and we recommend using
@@ -720,6 +747,20 @@ from the defaults. We've tried to specify what the defaults are below.
* 2 for normal (default) * 2 for normal (default)
* 3 for verbose * 3 for verbose
* `:debug_output`:
When enabled, the generated mock will emit a `TEST_MESSAGE` for each
mock setup call (e.g. `_Expect`, `_Ignore`, `_AddCallback`) and each
actual mock invocation. This can help diagnose the order in which mock
calls are being set up and executed during a test.
For example, with `:debug_output: true` and a mocked function `int foo(int a)`,
the generated mock will print messages such as:
CMock: foo_ExpectAndReturn called
CMock: mock foo called
* default: false
* `:weak`: * `:weak`:
When set this to some value, the generated mocks are defined as weak When set this to some value, the generated mocks are defined as weak
symbols using the configured format. This allows them to be overridden symbols using the configured format. This allows them to be overridden
@@ -929,8 +970,9 @@ that exposes them as virtual methods and modify your code to inject mocks at
run-time... but there is another way! run-time... but there is another way!
Simply use CMock to mock the static member methods and a C++ mocking framework Simply use CMock to mock the static member methods and a C++ mocking framework
to handle the virtual methods. (Yes, you can mix mocks from CMock and a C++ to handle the virtual methods. CMock does NOT mock non-static members. For those,
mocking framework together in the same test!) you'll need an actual C++ mocking framework. (Yes, you can mix mocks from CMock
and a C++ mocking framework together in the same test!)
Keep in mind that since C++ mocking frameworks often link the real object to the Keep in mind that since C++ mocking frameworks often link the real object to the
unit test too, we need to resolve multiple definition errors with something like unit test too, we need to resolve multiple definition errors with something like
+6 -1
View File
@@ -52,7 +52,12 @@ class CMockConfig
# - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff), # - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff),
# so we check for word boundaries when searching for them # so we check for word boundaries when searching for them
# - We first remove "static inline" combinations and boil down to single inline or static statements # - We first remove "static inline" combinations and boil down to single inline or static statements
:inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\binline\b)\s*', '(?:static\s*)?(?:__inline__)?__attribute__\s*\([ (]*always_inline[ )]*\)', 'static __inline__'] # Last part (\s*) is just to remove whitespaces (only to prettify the output) :inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\binline\b)\s*', '(?:static\s*)?(?:__inline__)?__attribute__\s*\([ (]*always_inline[ )]*\)', 'static __inline__'], # Last part (\s*) is just to remove whitespaces (only to prettify the output)
# Compile-time assertion macro names to strip entirely to avoid being confused with function prototypes
# Common C11, BSD, and embedded RTOS variants are included by default. Add custom names as needed.
:ct_assert_patterns => ['ct_assert', '_?[Ss]tatic_[Aa]ssert', 'STATIC_ASSERT', 'BUILD_ASSERT', 'CTASSERT'],
:debug_output => false
}.freeze }.freeze
def initialize(options = nil) def initialize(options = nil)
+2
View File
@@ -22,6 +22,7 @@ class CMockGenerator
@fail_on_unexpected_calls = @config.fail_on_unexpected_calls @fail_on_unexpected_calls = @config.fail_on_unexpected_calls
@exclude_setjmp_h = @config.exclude_setjmp_h @exclude_setjmp_h = @config.exclude_setjmp_h
@subdir = @config.subdir @subdir = @config.subdir
@debug_output = @config.debug_output
@includes_h_pre_orig_header = ((@config.includes || []) + (@config.includes_h_pre_orig_header || [])).uniq.map { |h| h =~ /</ ? h : "\"#{h}\"" } @includes_h_pre_orig_header = ((@config.includes || []) + (@config.includes_h_pre_orig_header || [])).uniq.map { |h| h =~ /</ ? h : "\"#{h}\"" }
@includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" } @includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
@@ -344,6 +345,7 @@ class CMockGenerator
file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n" file << " UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM;\n"
file << " CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance;\n" file << " CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance;\n"
file << " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" file << " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n"
file << " TEST_MESSAGE(\"CMock: mock #{function[:name]} called\");\n" if @debug_output
file << " cmock_call_instance = (CMOCK_#{function[:name]}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.#{function[:name]}_CallInstance);\n" file << " cmock_call_instance = (CMOCK_#{function[:name]}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.#{function[:name]}_CallInstance);\n"
file << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n" file << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n"
file << @plugins.run(:mock_precheck_return_thru_ptr, function) file << @plugins.run(:mock_precheck_return_thru_ptr, function)
+10 -1
View File
@@ -15,6 +15,7 @@ class CMockGeneratorPluginCallback
@priority = 6 @priority = 6
@include_count = @config.callback_include_count @include_count = @config.callback_include_count
@debug_output = @config.debug_output
end end
def instance_structure(function) def instance_structure(function)
@@ -33,7 +34,8 @@ class CMockGeneratorPluginCallback
"typedef #{return_type} (* CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \ "typedef #{return_type} (* CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \
"void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback);\n" \ "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback);\n" \
"void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback);\n" \ "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback);\n" \
"#define #{func_name}_StubWithCallback #{func_name}_#{action}\n" "#define #{func_name}_StubWithCallback #{func_name}_#{action}\n" \
"int #{func_name}_CallCount(void);\n"
end end
def generate_call(function) def generate_call(function)
@@ -71,12 +73,19 @@ class CMockGeneratorPluginCallback
has_ignore = @config.plugins.include? :ignore has_ignore = @config.plugins.include? :ignore
lines = '' lines = ''
lines << "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback)\n{\n" lines << "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback)\n{\n"
lines << " TEST_MESSAGE(\"CMock: #{func_name}_AddCallback called\");\n" if @debug_output
lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore
lines << " Mock.#{func_name}_CallbackBool = (char)1;\n" lines << " Mock.#{func_name}_CallbackBool = (char)1;\n"
lines << " Mock.#{func_name}_CallbackCalls = 0;\n"
lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n"
lines << "int #{func_name}_CallCount(void)\n{\n"
lines << " TEST_MESSAGE(\"CMock: #{func_name}_CallCount called\");\n" if @debug_output
lines << " return Mock.#{func_name}_CallbackCalls;\n}\n\n"
lines << "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback)\n{\n" lines << "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback)\n{\n"
lines << " TEST_MESSAGE(\"CMock: #{func_name}_Stub called\");\n" if @debug_output
lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore
lines << " Mock.#{func_name}_CallbackBool = (char)0;\n" lines << " Mock.#{func_name}_CallbackBool = (char)0;\n"
lines << " Mock.#{func_name}_CallbackCalls = 0;\n"
lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n"
end end
+3 -1
View File
@@ -17,6 +17,7 @@ class CMockGeneratorPluginExpect
@utils = utils @utils = utils
@unity_helper = @utils.helpers[:unity_helper] @unity_helper = @utils.helpers[:unity_helper]
@priority = 5 @priority = 5
@debug_output = @config.debug_output
if @config.plugins.include? :expect_any_args if @config.plugins.include? :expect_any_args
alias :mock_implementation :mock_implementation_might_check_args alias :mock_implementation :mock_implementation_might_check_args
@@ -30,7 +31,7 @@ class CMockGeneratorPluginExpect
lines << " #{function[:return][:type]} ReturnVal;\n" unless function[:return][:void?] lines << " #{function[:return][:type]} ReturnVal;\n" unless function[:return][:void?]
lines << " int CallOrder;\n" if @ordered lines << " int CallOrder;\n" if @ordered
function[:args].each do |arg| function[:args].each do |arg|
lines << " #{arg[:type]} Expected_#{arg[:name]};\n" lines << " #{arg[:volatile?] ? "volatile #{arg[:type]}" : arg[:type]} Expected_#{arg[:name]};\n"
end end
lines lines
end end
@@ -92,6 +93,7 @@ class CMockGeneratorPluginExpect
else else
"void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n" "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n"
end end
lines << " TEST_MESSAGE(\"CMock: #{func_name}_#{function[:return][:void?] ? 'Expect' : 'ExpectAndReturn'} called\");\n" if @debug_output
lines << @utils.code_add_base_expectation(func_name) lines << @utils.code_add_base_expectation(func_name)
lines << @utils.code_call_argument_loader(function) lines << @utils.code_call_argument_loader(function)
lines << @utils.code_assign_argument_quickly('cmock_call_instance->ReturnVal', function[:return]) unless function[:return][:void?] lines << @utils.code_assign_argument_quickly('cmock_call_instance->ReturnVal', function[:return]) unless function[:return][:void?]
@@ -13,6 +13,7 @@ class CMockGeneratorPluginExpectAnyArgs
@error_stubs = @config.create_error_stubs @error_stubs = @config.create_error_stubs
@utils = utils @utils = utils
@priority = 3 @priority = 3
@debug_output = @config.debug_output
end end
def instance_typedefs(_function) def instance_typedefs(_function)
@@ -43,6 +44,7 @@ class CMockGeneratorPluginExpectAnyArgs
else else
"void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n"
end end
lines << " TEST_MESSAGE(\"CMock: #{function[:name]}_#{function[:return][:void?] ? 'ExpectAnyArgs' : 'ExpectAnyArgsAndReturn'} called\");\n" if @debug_output
lines << @utils.code_add_base_expectation(function[:name], true) lines << @utils.code_add_base_expectation(function[:name], true)
unless function[:return][:void?] unless function[:return][:void?]
lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n"
+3
View File
@@ -13,6 +13,7 @@ class CMockGeneratorPluginIgnore
@error_stubs = @config.create_error_stubs @error_stubs = @config.create_error_stubs
@utils = utils @utils = utils
@priority = 2 @priority = 2
@debug_output = @config.debug_output
end end
def instance_structure(function) def instance_structure(function)
@@ -62,6 +63,7 @@ class CMockGeneratorPluginIgnore
else else
"void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n"
end end
lines << " TEST_MESSAGE(\"CMock: #{function[:name]}_#{function[:return][:void?] ? 'Ignore' : 'IgnoreAndReturn'} called\");\n" if @debug_output
unless function[:return][:void?] unless function[:return][:void?]
lines << @utils.code_add_base_expectation(function[:name], false) lines << @utils.code_add_base_expectation(function[:name], false)
end end
@@ -73,6 +75,7 @@ class CMockGeneratorPluginIgnore
# Add stop ignore function. it does not matter if there are any args # Add stop ignore function. it does not matter if there are any args
lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n" lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n"
lines << " TEST_MESSAGE(\"CMock: #{function[:name]}_StopIgnore called\");\n" if @debug_output
unless function[:return][:void?] unless function[:return][:void?]
lines << " if(Mock.#{function[:name]}_IgnoreBool)\n" lines << " if(Mock.#{function[:name]}_IgnoreBool)\n"
lines << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n" lines << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n"
+3 -1
View File
@@ -9,9 +9,10 @@ class CMockGeneratorPluginIgnoreArg
attr_reader :priority attr_reader :priority
attr_accessor :utils attr_accessor :utils
def initialize(_config, utils) def initialize(config, utils)
@utils = utils @utils = utils
@priority = 10 @priority = 10
@debug_output = config.debug_output
end end
def instance_typedefs(function) def instance_typedefs(function)
@@ -38,6 +39,7 @@ class CMockGeneratorPluginIgnoreArg
function[:args].each do |arg| function[:args].each do |arg|
lines << "void #{func_name}_CMockIgnoreArg_#{arg[:name]}(UNITY_LINE_TYPE cmock_line)\n" lines << "void #{func_name}_CMockIgnoreArg_#{arg[:name]}(UNITY_LINE_TYPE cmock_line)\n"
lines << "{\n" lines << "{\n"
lines << " TEST_MESSAGE(\"CMock: #{func_name}_IgnoreArg_#{arg[:name]} called\");\n" if @debug_output
lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \ lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \
"(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n"
lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp);\n" lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp);\n"
@@ -13,6 +13,7 @@ class CMockGeneratorPluginIgnoreStateless
@error_stubs = @config.create_error_stubs @error_stubs = @config.create_error_stubs
@utils = utils @utils = utils
@priority = 2 @priority = 2
@debug_output = @config.debug_output
end end
def instance_structure(function) def instance_structure(function)
@@ -63,6 +64,7 @@ class CMockGeneratorPluginIgnoreStateless
else else
"void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]})\n{\n" "void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]})\n{\n"
end end
lines << " TEST_MESSAGE(\"CMock: #{function[:name]}_#{function[:return][:void?] ? 'Ignore' : 'IgnoreAndReturn'} called\");\n" if @debug_output
unless function[:return][:void?] unless function[:return][:void?]
lines << " Mock.#{function[:name]}_CallInstance = CMOCK_GUTS_NONE;\n" lines << " Mock.#{function[:name]}_CallInstance = CMOCK_GUTS_NONE;\n"
lines << " Mock.#{function[:name]}_FinalReturn = cmock_to_return;\n" lines << " Mock.#{function[:name]}_FinalReturn = cmock_to_return;\n"
@@ -72,6 +74,7 @@ class CMockGeneratorPluginIgnoreStateless
# Add stop ignore function. it does not matter if there are any args # Add stop ignore function. it does not matter if there are any args
lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n" lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n"
lines << " TEST_MESSAGE(\"CMock: #{function[:name]}_StopIgnore called\");\n" if @debug_output
lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n" lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n"
lines << "}\n\n" lines << "}\n\n"
@@ -13,6 +13,7 @@ class CMockGeneratorPluginReturnThruPtr
@utils = utils @utils = utils
@priority = 9 @priority = 9
@config = config @config = config
@debug_output = @config.debug_output
plugins = @config.plugins plugins = @config.plugins
@ignore_used = plugins.include?(:ignore) || plugins.include?(:ignore_stateless) @ignore_used = plugins.include?(:ignore) || plugins.include?(:ignore_stateless)
end end
@@ -99,6 +100,7 @@ class CMockGeneratorPluginReturnThruPtr
lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{ptr_to_const(arg[:type])} #{arg_name}, size_t cmock_size)\n" lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{ptr_to_const(arg[:type])} #{arg_name}, size_t cmock_size)\n"
lines << "{\n" lines << "{\n"
lines << " TEST_MESSAGE(\"CMock: #{func_name}_ReturnThruPtr_#{arg_name} called\");\n" if @debug_output
lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \ lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \
"(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n"
if @ignore_used if @ignore_used
+7 -6
View File
@@ -25,12 +25,13 @@ class CMockGeneratorUtils
end end
def self.arg_type_with_const(arg) def self.arg_type_with_const(arg)
# Restore any "const" that was removed in header parsing # Restore any "const" or "volatile" that was removed in header parsing
if arg[:type].include?('*') type = if arg[:type].include?('*')
arg[:const_ptr?] ? "#{arg[:type]} const" : arg[:type] arg[:const_ptr?] ? "#{arg[:type]} const" : arg[:type]
else else
arg[:const?] ? "const #{arg[:type]}" : arg[:type] arg[:const?] ? "const #{arg[:type]}" : arg[:type]
end end
arg[:volatile?] ? "volatile #{type}" : type
end end
def arg_type_with_const(arg) def arg_type_with_const(arg)
+72 -10
View File
@@ -15,7 +15,7 @@ class CMockHeaderParser
@c_calling_conventions = cfg.c_calling_conventions.uniq @c_calling_conventions = cfg.c_calling_conventions.uniq
@treat_as_array = cfg.treat_as_array @treat_as_array = cfg.treat_as_array
@treat_as_void = (['void'] + cfg.treat_as_void).uniq @treat_as_void = (['void'] + cfg.treat_as_void).uniq
@function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]*?\w[\w\s\*\(\),\[\]]*?)\(([\w\s\*\(\),\.\[\]+\-\/]*)\)' @function_declaration_parse_base_match = '([^(]*\w)\s*\(([\w\s\*\(\),\.\[\]+\-\/]*)\)'
@declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m @declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m
@standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq @standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq
@array_size_name = cfg.array_size_name @array_size_name = cfg.array_size_name
@@ -26,6 +26,7 @@ class CMockHeaderParser
@treat_externs = cfg.treat_externs @treat_externs = cfg.treat_externs
@treat_inlines = cfg.treat_inlines @treat_inlines = cfg.treat_inlines
@inline_function_patterns = cfg.inline_function_patterns @inline_function_patterns = cfg.inline_function_patterns
@ct_assert_patterns = cfg.ct_assert_patterns
@c_strippables += ['extern'] if @treat_externs == :include # we'll need to remove the attribute if we're allowing externs @c_strippables += ['extern'] if @treat_externs == :include # we'll need to remove the attribute if we're allowing externs
@c_strippables += ['inline'] if @treat_inlines == :include # we'll need to remove the attribute if we're allowing inlines @c_strippables += ['inline'] if @treat_inlines == :include # we'll need to remove the attribute if we're allowing inlines
end end
@@ -64,6 +65,48 @@ class CMockHeaderParser
private if $ThisIsOnlyATest.nil? ################ private if $ThisIsOnlyATest.nil? ################
# Remove code disabled by basic preprocesser tags like #if 0, #if 1, etc.
def remove_disabled_code_from_source(source)
result = []
emit = true
# Stack entries: [restore_emit, else_emit, seen_else]
# restore_emit: value to restore `emit` to after matching #endif
# else_emit: value to set `emit` to after #else
# seen_else: whether #else at this level has already been seen
stack = []
source.each_line do |line|
stripped = line.strip
if stripped =~ /^#\s*if\s+0\s*$/
stack.push([emit, emit, false])
emit = false
elsif stripped =~ /^#\s*if\s+1\s*$/
stack.push([emit, false, false])
# emit unchanged: keep the if-branch
elsif stripped =~ /^#\s*if/
stack.push([emit, emit, false])
# emit unchanged: unknown condition, keep both branches
elsif stripped =~ /^#\s*else\b/
unless stack.empty?
entry = stack.last
unless entry[2]
stack[-1] = [entry[0], entry[1], true]
emit = entry[1]
end
end
elsif stripped =~ /^#\s*endif\b/
unless stack.empty?
entry = stack.pop
emit = entry[0]
end
elsif emit
result << line
end
end
result.join
end
# Remove C/C++ comments from a string # Remove C/C++ comments from a string
# +source+:: String which will have the comments removed # +source+:: String which will have the comments removed
def remove_comments_from_source(source) def remove_comments_from_source(source)
@@ -75,15 +118,13 @@ class CMockHeaderParser
def remove_nested_pairs_of_braces(source) def remove_nested_pairs_of_braces(source)
# remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection) # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection)
if RUBY_VERSION.split('.')[0].to_i > 1 # Collapse innermost brace pairs first using a brace-free sentinel (\x00), working
# we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash. # outward until no balanced pairs remain. This avoids the catastrophic backtracking
r = '\\{([^\\{\\}]*|\\g<0>)*\\}' # of the recursive regex \{([^\{\}]*|\g<0>)*\} on Ruby < 3.2 while preserving
source.gsub!(/#{r}/m, '{ }') # identical semantics: every balanced brace structure is collapsed to '{ }'.
else while source.gsub!(/\{[^{}]*\}/m, "\x00")
while source.gsub!(/\{[^{}]*\{[^{}]*\}[^{}]*\}/m, '{ }')
end
end end
source.gsub!("\x00", '{ }')
source source
end end
@@ -248,6 +289,10 @@ class CMockHeaderParser
# remove preprocessor statements and extern "C" # remove preprocessor statements and extern "C"
source.gsub!(/extern\s+"C"\s*\{/, '') source.gsub!(/extern\s+"C"\s*\{/, '')
# handle basic literal preprocessor conditionals before stripping all directives
source = remove_disabled_code_from_source(source)
source.gsub!(/^\s*#.*/, '') source.gsub!(/^\s*#.*/, '')
# enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them
@@ -260,6 +305,12 @@ class CMockHeaderParser
source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2') source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2')
source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp
# strip calls to known compile-time assertion macros by name -- never function prototypes regardless of argument form
source.gsub!(/\b(?:#{@ct_assert_patterns.join('|')})\s*\([^;]*\)/, '') unless @ct_assert_patterns.empty?
# strip any remaining WORD(...==...) etc. -- calls containing comparison operators cannot be C function prototypes
# must run before default-value removal, which would corrupt "!= 0" into "!" by removing "= 0"
source.gsub!(/\b\w+\s*\((?:[^()!=<>]|\([^()]*\))*(?:==|!=|<=|>=)[^;]*\)/, '')
source.gsub!(/\s*=\s*['"a-zA-Z0-9_.]+\s*/, '') # remove default value statements from argument lists source.gsub!(/\s*=\s*['"a-zA-Z0-9_.]+\s*/, '') # remove default value statements from argument lists
# strip macro decorator patterns that cannot be C function prototypes. # strip macro decorator patterns that cannot be C function prototypes.
@@ -277,7 +328,7 @@ class CMockHeaderParser
source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/, '\1') unless @c_strippables.empty? # remove known attributes slated to be stripped source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/, '\1') unless @c_strippables.empty? # remove known attributes slated to be stripped
# scan standalone function pointers and remove them, because they can just be ignored # scan standalone function pointers and remove them, because they can just be ignored
source.gsub!(/\w+\s*\(\s*\*\s*\w+\s*\)\s*\([^)]*\)\s*;/, ';') source.gsub!(/\w[\w\s*]*\(\s*\*\s*\w+\s*\)\s*\([^)]*\)\s*;/, ';')
# scan for functions which return function pointers, because they are a pain # scan for functions which return function pointers, because they are a pain
source.gsub!(/([\w\s*]+)\(*\(\s*\*([\w\s*]+)\s*\(([\w\s*,]*)\)\)\s*\(([\w\s*,]*)\)\)*/) do |_m| source.gsub!(/([\w\s*]+)\(*\(\s*\*([\w\s*]+)\s*\(([\w\s*,]*)\)\)\s*\(([\w\s*,]*)\)\)*/) do |_m|
@@ -449,6 +500,11 @@ class CMockHeaderParser
arg_info.delete(:modifier) # don't care about this arg_info.delete(:modifier) # don't care about this
arg_info.delete(:c_calling_convention) # don't care about this arg_info.delete(:c_calling_convention) # don't care about this
# Strip volatile from pointer-to-volatile arg types so internal storage and
# comparisons use the clean type; volatile? flag lets generators reconstruct
# it where needed (e.g. function signatures via arg_type_with_const).
arg_info[:type] = arg_info[:type].gsub(/\bvolatile\s*/, '').gsub(/\s+\*/, '*').strip if arg_info[:volatile?]
arg_info[:array_dims] = array_dims_by_name[arg_info[:name]] if array_dims_by_name.key?(arg_info[:name]) arg_info[:array_dims] = array_dims_by_name[arg_info[:name]] if array_dims_by_name.key?(arg_info[:name])
# Handle pointer-to-array args: (*name)[dims] was rewritten to * name before clean_args # Handle pointer-to-array args: (*name)[dims] was rewritten to * name before clean_args
@@ -542,12 +598,18 @@ class CMockHeaderParser
end end
end end
def divine_volatile(arg)
# only flag pointer types where volatile applies to the pointed-to type (before the last *)
arg.include?('*') && (/(^|\s|\*)volatile(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg ? true : false)
end
def divine_ptr_and_const(arg) def divine_ptr_and_const(arg)
divination = {} divination = {}
divination[:ptr?] = divine_ptr(arg) divination[:ptr?] = divine_ptr(arg)
divination[:string?] = !divination[:ptr?] && (/(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg ? true : false) divination[:string?] = !divination[:ptr?] && (/(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg ? true : false)
divination[:const?] = divine_const(arg) divination[:const?] = divine_const(arg)
divination[:volatile?] = true if divine_volatile(arg)
# an arg containing "const" after the last * is a constant pointer # an arg containing "const" after the last * is a constant pointer
divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false
+4
View File
@@ -25,8 +25,12 @@ const char* CMockStringMismatch = "Function called with unexpected argument v
static unsigned char* CMock_Guts_Buffer = NULL; static unsigned char* CMock_Guts_Buffer = NULL;
static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_ALIGN_SIZE; static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_ALIGN_SIZE;
static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE;
#else
#if !defined(UNITY_EXCLUDE_STDDEF_H) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
static _Alignas(max_align_t) long long CMock_Guts_Space[(CMOCK_MEM_SIZE + CMOCK_MEM_ALIGN_SIZE + sizeof(long long) - 1) / sizeof(long long)];
#else #else
static long long CMock_Guts_Space[(CMOCK_MEM_SIZE + CMOCK_MEM_ALIGN_SIZE + sizeof(long long) - 1) / sizeof(long long)]; static long long CMock_Guts_Space[(CMOCK_MEM_SIZE + CMOCK_MEM_ALIGN_SIZE + sizeof(long long) - 1) / sizeof(long long)];
#endif
static unsigned char* CMock_Guts_Buffer = (unsigned char*)CMock_Guts_Space; static unsigned char* CMock_Guts_Buffer = (unsigned char*)CMock_Guts_Space;
static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_SIZE + CMOCK_MEM_ALIGN_SIZE;//sizeof(CMock_Guts_Space); static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_SIZE + CMOCK_MEM_ALIGN_SIZE;//sizeof(CMock_Guts_Space);
static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE;
+2 -2
View File
@@ -11,8 +11,8 @@
#include "cmock_internals.h" #include "cmock_internals.h"
#define CMOCK_VERSION_MAJOR 2 #define CMOCK_VERSION_MAJOR 2
#define CMOCK_VERSION_MINOR 6 #define CMOCK_VERSION_MINOR 7
#define CMOCK_VERSION_BUILD 4 #define CMOCK_VERSION_BUILD 0
#define CMOCK_VERSION ((CMOCK_VERSION_MAJOR << 16) | (CMOCK_VERSION_MINOR << 8) | CMOCK_VERSION_BUILD) #define CMOCK_VERSION ((CMOCK_VERSION_MAJOR << 16) | (CMOCK_VERSION_MINOR << 8) | CMOCK_VERSION_BUILD)
/* should be big enough to index full range of CMOCK_MEM_MAX */ /* should be big enough to index full range of CMOCK_MEM_MAX */
+23
View File
@@ -76,6 +76,18 @@ extern const char* CMockStringMismatch;
#else #else
#define CMOCK_MEM_ALIGN (2) #define CMOCK_MEM_ALIGN (2)
#endif #endif
/* Boost to 8-byte alignment when 64-bit integers or doubles are enabled,
* since those types require 8-byte alignment even on 32-bit platforms */
#if CMOCK_MEM_ALIGN < 3
#if defined(UNITY_SUPPORT_64) || defined(UNITY_INCLUDE_DOUBLE)
#undef CMOCK_MEM_ALIGN
#define CMOCK_MEM_ALIGN (3)
#endif
#endif
/* sentinel: CMOCK_MEM_ALIGN was auto-detected (not user-supplied) */
#define CMOCK_MEM_ALIGN_AUTO
#endif #endif
/* amount of memory to allow cmock to use in its internal heap */ /* amount of memory to allow cmock to use in its internal heap */
@@ -98,6 +110,17 @@ extern const char* CMockStringMismatch;
/* automatically calculated defs for easier reading */ /* automatically calculated defs for easier reading */
#define CMOCK_MEM_ALIGN_SIZE (CMOCK_MEM_INDEX_TYPE)(1u << CMOCK_MEM_ALIGN) #define CMOCK_MEM_ALIGN_SIZE (CMOCK_MEM_INDEX_TYPE)(1u << CMOCK_MEM_ALIGN)
#define CMOCK_MEM_ALIGN_MASK (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_ALIGN_SIZE - 1) #define CMOCK_MEM_ALIGN_MASK (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_ALIGN_SIZE - 1)
/* When CMOCK_MEM_ALIGN was auto-detected and stddef.h is available (C11+),
* use sizeof(max_align_t) to guarantee alignment is sufficient for all
* fundamental types on this platform, taking the larger of the two values */
#if defined(CMOCK_MEM_ALIGN_AUTO) && !defined(UNITY_EXCLUDE_STDDEF_H) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
#undef CMOCK_MEM_ALIGN_SIZE
#undef CMOCK_MEM_ALIGN_MASK
#define CMOCK_MEM_ALIGN_SIZE (CMOCK_MEM_INDEX_TYPE)(sizeof(max_align_t) > (1u << CMOCK_MEM_ALIGN) ? sizeof(max_align_t) : (1u << CMOCK_MEM_ALIGN))
#define CMOCK_MEM_ALIGN_MASK (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_ALIGN_SIZE - 1)
#endif
#define CMOCK_MEM_INDEX_SIZE (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_PTR_AS_INT)((sizeof(CMOCK_MEM_INDEX_TYPE) > CMOCK_MEM_ALIGN_SIZE) ? sizeof(CMOCK_MEM_INDEX_TYPE) : CMOCK_MEM_ALIGN_SIZE) #define CMOCK_MEM_INDEX_SIZE (CMOCK_MEM_INDEX_TYPE)(CMOCK_MEM_PTR_AS_INT)((sizeof(CMOCK_MEM_INDEX_TYPE) > CMOCK_MEM_ALIGN_SIZE) ? sizeof(CMOCK_MEM_INDEX_TYPE) : CMOCK_MEM_ALIGN_SIZE)
+48
View File
@@ -0,0 +1,48 @@
# =========================================================================
# CMock - Automatic Mock Generation for C
# ThrowTheSwitch.org
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
# SPDX-License-Identifier: MIT
# =========================================================================
# gcc_64 with debug symbols enabled for meaningful valgrind output.
# Used by the CI valgrind job to check for memory leaks and errors.
---
:tools:
:test_compiler:
:name: compiler
:executable: gcc
:arguments:
- "-c"
- "-m64"
- "-g"
- "-Wall"
- "-Wno-address"
- "-std=c99"
- "-pedantic"
- '-I"${5}"'
- "-D${6}"
- "${1}"
- "-o ${2}"
:test_linker:
:name: linker
:executable: gcc
:arguments:
- "${1}"
- "-lm"
- "-m64"
- "-o ${2}"
:extension:
:object: ".o"
:executable: ".exe"
:defines:
:test:
- UNITY_EXCLUDE_STDINT_H
- UNITY_EXCLUDE_LIMITS_H
- UNITY_INCLUDE_DOUBLE
- UNITY_SUPPORT_TEST_CASES
- UNITY_SUPPORT_64
- UNITY_INT_WIDTH=32
- UNITY_LONG_WIDTH=64
- UNITY_POINTER_WIDTH=64
+9 -2
View File
@@ -80,7 +80,7 @@ module RakefileHelpers
raise "Cannot find Config File #{config_target}" raise "Cannot find Config File #{config_target}"
end end
$colour_output = $proj[:project][:colour] $colour_output = $proj[:project][:colour] && !ENV['NO_COLOR']
end end
def configure_clean def configure_clean
@@ -411,7 +411,9 @@ module RakefileHelpers
test_file = 'test_' + File.basename(test_case).ext(C_EXTENSION) test_file = 'test_' + File.basename(test_case).ext(C_EXTENSION)
result_file = test_file.ext(RESULT_EXTENSION) result_file = test_file.ext(RESULT_EXTENSION)
test_results = File.readlines(SYSTEST_BUILD_FILES_PATH + result_file).reject {|line| line.size < 10 } all_results = File.readlines(SYSTEST_BUILD_FILES_PATH + result_file).reject {|line| line.size < 10 }
info_results = all_results.select {|line| line =~ /:INFO:/}
test_results = all_results.reject {|line| line =~ /:INFO:/}
tests.each_with_index do |test, index| tests.each_with_index do |test, index|
this_failed = case(test[:pass]) this_failed = case(test[:pass])
when :ignore when :ignore
@@ -432,6 +434,11 @@ module RakefileHelpers
new_msg = "#{test_file}:test#{index+1}:should #{test[:should]}:should have output matching '#{test[:verify_error]}' but was '#{test_results[index]}'" new_msg = "#{test_file}:test#{index+1}:should #{test[:should]}:should have output matching '#{test[:verify_error]}' but was '#{test_results[index]}'"
failure_messages << new_msg failure_messages << new_msg
report new_msg report new_msg
elsif (test[:verify_message]) and not info_results.any? {|line| line =~ /test#{index+1}:INFO:.*#{test[:verify_message]}/}
total_failures += 1
new_msg = "#{test_file}:test#{index+1}:should #{test[:should]}:should have message matching '#{test[:verify_message]}' but none found in: #{info_results.select{|l| l =~ /test#{index+1}:/}.inspect}"
failure_messages << new_msg
report new_msg
else else
report "#{test_file}:test#{index+1}:should #{test[:should]}:PASS" report "#{test_file}:test#{index+1}:should #{test[:should]}:PASS"
end end
@@ -0,0 +1,235 @@
# =========================================================================
# CMock - Automatic Mock Generation for C
# ThrowTheSwitch.org
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
# SPDX-License-Identifier: MIT
# =========================================================================
---
:cmock:
:debug_output: true
:plugins:
- :ignore
- :callback
- :expect_any_args
- :return_thru_ptr
- :ignore_arg
:systest:
:types: |
:mockable: |
void foo(int a);
int bar(void);
int qux(int a);
void baz(int* p);
:source:
:header: |
void function(int a);
:code: |
void function(int a)
{
int temp = 0;
foo(a);
foo(bar());
qux(a);
baz(&temp);
}
:tests:
:common: |
void setUp(void) {}
void tearDown(void) {}
void my_foo_callback(int a, int cmock_num_calls) { (void)a; (void)cmock_num_calls; }
:units:
- :pass: TRUE
:should: 'output a debug message when foo_Expect is called'
:verify_message: 'CMock: foo_Expect called'
:code: |
test()
{
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when mock foo is called'
:verify_message: 'CMock: mock foo called'
:code: |
test()
{
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when bar_ExpectAndReturn is called'
:verify_message: 'CMock: bar_ExpectAndReturn called'
:code: |
test()
{
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when mock bar is called'
:verify_message: 'CMock: mock bar called'
:code: |
test()
{
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when foo_Ignore is called'
:verify_message: 'CMock: foo_Ignore called'
:code: |
test()
{
foo_Ignore();
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(99);
}
- :pass: TRUE
:should: 'output a debug message when bar_IgnoreAndReturn is called'
:verify_message: 'CMock: bar_IgnoreAndReturn called'
:code: |
test()
{
foo_Expect(1);
foo_Expect(42);
bar_IgnoreAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when foo_StopIgnore is called'
:verify_message: 'CMock: foo_StopIgnore called'
:code: |
test()
{
foo_Ignore();
foo_StopIgnore();
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when foo_ExpectAnyArgs is called'
:verify_message: 'CMock: foo_ExpectAnyArgs called'
:code: |
test()
{
foo_ExpectAnyArgs();
foo_ExpectAnyArgs();
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when qux_ExpectAnyArgsAndReturn is called'
:verify_message: 'CMock: qux_ExpectAnyArgsAndReturn called'
:code: |
test()
{
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_ExpectAnyArgsAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when foo_AddCallback is called'
:verify_message: 'CMock: foo_AddCallback called'
:code: |
test()
{
foo_AddCallback(my_foo_callback);
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when foo_Stub is called'
:verify_message: 'CMock: foo_Stub called'
:code: |
test()
{
foo_Stub(my_foo_callback);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
- :pass: TRUE
:should: 'output a debug message when baz_ReturnThruPtr_p is called'
:verify_message: 'CMock: baz_ReturnThruPtr_p called'
:code: |
test()
{
int val = 99;
foo_Expect(1);
foo_Expect(42);
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Expect(NULL);
baz_IgnoreArg_p();
baz_ReturnThruPtr_p(&val);
function(1);
}
- :pass: TRUE
:should: 'output a debug message when foo_IgnoreArg_a is called'
:verify_message: 'CMock: foo_IgnoreArg_a called'
:code: |
test()
{
foo_Expect(999);
foo_IgnoreArg_a();
foo_Expect(999);
foo_IgnoreArg_a();
bar_ExpectAndReturn(42);
qux_IgnoreAndReturn(0);
baz_Ignore();
function(1);
}
...
@@ -0,0 +1,291 @@
# =========================================================================
# CMock - Automatic Mock Generation for C
# ThrowTheSwitch.org
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
# SPDX-License-Identifier: MIT
# =========================================================================
---
:cmock:
:mock_path: test/mocks
:mock_prefix: mock_
:plugins:
- :array
- :cexception
- :ignore
- :callback
- :return_thru_ptr
- :ignore_arg
- :expect_any_args
:callback_after_arg_check: true
:callback_include_count: false
:systest:
:types: |
typedef struct {
int x;
int y;
} point_t;
:mockable: |
#include "CException.h"
void update_point(volatile point_t *p);
void update_points(volatile point_t *p, int n);
void update_int(volatile int *v);
int get_value(volatile point_t *p);
void mixed_volatile(int a, volatile int *v);
:source:
:header: |
#include "CException.h"
:code: |
:tests:
:common: |
#include "CException.h"
void setUp(void) {}
void tearDown(void) {}
void my_update_point_callback(volatile point_t *p) { p->x += 1; }
void my_update_int_callback(volatile int *v) { *v = 99; }
void my_update_points_callback(volatile point_t *p, int n)
{
int i;
for (i = 0; i < n; i++) { p[i].x = 100 + i; }
}
:units:
# --- expect plugin (base) ---
- :pass: TRUE
:should: "Expect passes when volatile struct pointer arg matches"
:code: |
test()
{
volatile point_t p = { 1, 2 };
update_point_Expect(&p);
update_point(&p);
}
- :pass: FALSE
:should: "Expect fails when volatile struct pointer arg does not match"
:code: |
test()
{
volatile point_t p = { 1, 2 };
volatile point_t wrong = { 9, 9 };
update_point_Expect(&wrong);
update_point(&p);
}
- :pass: TRUE
:should: "ExpectAndReturn works with a volatile struct pointer arg"
:code: |
test()
{
volatile point_t p = { 5, 7 };
get_value_ExpectAndReturn(&p, 42);
TEST_ASSERT_EQUAL(42, get_value(&p));
}
# --- return_thru_ptr plugin ---
- :pass: TRUE
:should: "ReturnThruPtr writes through a volatile struct pointer arg"
:code: |
test()
{
volatile point_t p = { 0, 0 };
point_t result = { 10, 20 };
update_point_Expect(&p);
update_point_ReturnThruPtr_p(&result);
update_point(&p);
TEST_ASSERT_EQUAL(10, p.x);
TEST_ASSERT_EQUAL(20, p.y);
}
- :pass: TRUE
:should: "ReturnThruPtr writes through a volatile int pointer arg"
:code: |
test()
{
volatile int v = 0;
int result = 42;
update_int_Expect(&v);
update_int_ReturnThruPtr_v(&result);
update_int(&v);
TEST_ASSERT_EQUAL(42, v);
}
- :pass: TRUE
:should: "ReturnThruPtr macros are defined for volatile pointer args"
:code: |
test()
{
#if !defined(update_point_ReturnThruPtr_p)
TEST_FAIL_MESSAGE("ReturnThruPtr not defined for volatile struct pointer arg.");
#endif
#if !defined(update_int_ReturnThruPtr_v)
TEST_FAIL_MESSAGE("ReturnThruPtr not defined for volatile int pointer arg.");
#endif
}
# --- array plugin ---
- :pass: TRUE
:should: "ExpectWithArray passes when all elements of a volatile array match"
:code: |
test()
{
volatile point_t p[3] = { {1,2}, {3,4}, {5,6} };
point_t expected[3] = { {1,2}, {3,4}, {5,6} };
update_points_ExpectWithArray(expected, 3, 3);
update_points(p, 3);
}
- :pass: FALSE
:should: "ExpectWithArray fails when one element of a volatile array does not match"
:code: |
test()
{
volatile point_t p[3] = { {1,2}, {3,4}, {5,6} };
point_t expected[3] = { {1,2}, {3,4}, {5,9} };
update_points_ExpectWithArray(expected, 3, 3);
update_points(p, 3);
}
# --- ignore plugin ---
- :pass: TRUE
:should: "Ignore suppresses calls to a volatile-arg function"
:code: |
test()
{
volatile point_t p = { 1, 2 };
update_point_Ignore();
update_point(&p);
update_point(&p);
}
- :pass: TRUE
:should: "IgnoreAndReturn suppresses calls and returns value for volatile-arg function"
:code: |
test()
{
volatile point_t p = { 1, 2 };
get_value_IgnoreAndReturn(77);
TEST_ASSERT_EQUAL(77, get_value(&p));
TEST_ASSERT_EQUAL(77, get_value(&p));
}
# --- ignore_arg plugin ---
- :pass: TRUE
:should: "IgnoreArg allows any value for the volatile pointer arg"
:code: |
test()
{
volatile int v1 = 10;
volatile int v2 = 20;
mixed_volatile_Expect(5, &v1);
mixed_volatile_IgnoreArg_v();
mixed_volatile(5, &v2);
}
- :pass: FALSE
:should: "IgnoreArg ignores volatile arg but still checks other args"
:code: |
test()
{
volatile int v = 10;
mixed_volatile_Expect(5, &v);
mixed_volatile_IgnoreArg_v();
mixed_volatile(99, &v);
}
# --- expect_any_args plugin ---
- :pass: TRUE
:should: "ExpectAnyArgs accepts any volatile array regardless of contents"
:code: |
test()
{
volatile point_t p1[2] = { {1,2}, {3,4} };
volatile point_t p2[2] = { {9,9}, {8,8} };
update_points_ExpectAnyArgs();
update_points_ExpectAnyArgs();
update_points(p1, 2);
update_points(p2, 2);
}
- :pass: TRUE
:should: "ExpectAnyArgsAndReturn works with a volatile array arg"
:code: |
test()
{
volatile point_t p[2] = { {1,2}, {3,4} };
get_value_ExpectAnyArgsAndReturn(55);
TEST_ASSERT_EQUAL(55, get_value(p));
}
- :pass: TRUE
:should: "ExpectAnyArgs and ReturnThruPtr can be combined on a volatile pointer arg"
:code: |
test()
{
volatile point_t p = { 0, 0 };
point_t result = { 7, 8 };
update_point_ExpectAnyArgs();
update_point_ReturnThruPtr_p(&result);
update_point(&p);
TEST_ASSERT_EQUAL(7, p.x);
TEST_ASSERT_EQUAL(8, p.y);
}
# --- callback plugin ---
- :pass: TRUE
:should: "StubWithCallback can read and write all elements of a volatile array"
:code: |
test()
{
volatile point_t p[3] = { {0,0}, {0,0}, {0,0} };
point_t expected[3] = { {0,0}, {0,0}, {0,0} };
update_points_Expect(expected, 3);
update_points_StubWithCallback(my_update_points_callback);
update_points(p, 3);
TEST_ASSERT_EQUAL(100, p[0].x);
TEST_ASSERT_EQUAL(101, p[1].x);
TEST_ASSERT_EQUAL(102, p[2].x);
}
- :pass: TRUE
:should: "StubWithCallback receives a volatile int pointer and can modify it"
:code: |
test()
{
volatile int v = 0;
update_int_Expect(&v);
update_int_StubWithCallback(my_update_int_callback);
update_int(&v);
TEST_ASSERT_EQUAL(99, v);
}
# --- cexception plugin ---
- :pass: TRUE
:should: "ExpectAndThrow throws when a volatile-arg function is called"
:code: |
test()
{
CEXCEPTION_T e = 0;
volatile point_t p = { 1, 2 };
update_point_ExpectAndThrow(&p, 42);
Try {
update_point(&p);
TEST_FAIL_MESSAGE("Expected exception was not thrown.");
} Catch(e) {
TEST_ASSERT_EQUAL(42, e);
}
}
+2
View File
@@ -50,6 +50,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do
@config.expect :fail_on_unexpected_calls, true @config.expect :fail_on_unexpected_calls, true
@config.expect :treat_inlines, :exclude @config.expect :treat_inlines, :exclude
@config.expect :exclude_setjmp_h, false @config.expect :exclude_setjmp_h, false
@config.expect :debug_output, false
@cmock_generator = CMockGenerator.new(@config, @file_writer, @utils, @plugins) @cmock_generator = CMockGenerator.new(@config, @file_writer, @utils, @plugins)
@cmock_generator.module_name = @module_name @cmock_generator.module_name = @module_name
@cmock_generator.module_ext = '.h' @cmock_generator.module_ext = '.h'
@@ -71,6 +72,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do
@config.expect :fail_on_unexpected_calls, true @config.expect :fail_on_unexpected_calls, true
@config.expect :treat_inlines, :exclude @config.expect :treat_inlines, :exclude
@config.expect :exclude_setjmp_h, false @config.expect :exclude_setjmp_h, false
@config.expect :debug_output, false
@cmock_generator_strict = CMockGenerator.new(@config, @file_writer, @utils, @plugins) @cmock_generator_strict = CMockGenerator.new(@config, @file_writer, @utils, @plugins)
@test_project = { @test_project = {
@@ -12,7 +12,7 @@ describe CMockGeneratorPluginExpectAnyArgs, "Verify Generation Of Mock Function
before do before do
create_mocks :config, :utils create_mocks :config, :utils
@config = create_stub(:respond_to? => true, :create_error_stubs => false) @config = create_stub(:respond_to? => true, :create_error_stubs => false, :debug_output => false)
@cmock_generator_plugin_expect_any_args = CMockGeneratorPluginExpectAnyArgs.new(@config, @utils) @cmock_generator_plugin_expect_any_args = CMockGeneratorPluginExpectAnyArgs.new(@config, @utils)
end end
@@ -18,7 +18,8 @@ describe CMockGeneratorPluginExpect, "Verify Generation Of Mock Function Declara
:enforce_strict_ordering => false, :enforce_strict_ordering => false,
:respond_to? => true, :respond_to? => true,
:create_error_stubs => false, :create_error_stubs => false,
:plugins => [ :expect ] ) :plugins => [ :expect ],
:debug_output => false )
@utils.expect :helpers, {} @utils.expect :helpers, {}
@cmock_generator_plugin_expect = CMockGeneratorPluginExpect.new(@config, @utils) @cmock_generator_plugin_expect = CMockGeneratorPluginExpect.new(@config, @utils)
@@ -12,7 +12,7 @@ describe CMockGeneratorPluginIgnoreStateless, "Verify Generation Of Mock Functio
before do before do
create_mocks :config, :utils create_mocks :config, :utils
@config = create_stub(:respond_to? => true, :create_error_stubs => false) @config = create_stub(:respond_to? => true, :create_error_stubs => false, :debug_output => false)
@cmock_generator_plugin_ignore_stateless = CMockGeneratorPluginIgnoreStateless.new(@config, @utils) @cmock_generator_plugin_ignore_stateless = CMockGeneratorPluginIgnoreStateless.new(@config, @utils)
end end
@@ -12,7 +12,7 @@ describe CMockGeneratorPluginIgnore, "Verify Generation Of Mock Function Declara
before do before do
create_mocks :config, :utils create_mocks :config, :utils
@config = create_stub(:respond_to? => true, :create_error_stubs => false) @config = create_stub(:respond_to? => true, :create_error_stubs => false, :debug_output => false)
@cmock_generator_plugin_ignore = CMockGeneratorPluginIgnore.new(@config, @utils) @cmock_generator_plugin_ignore = CMockGeneratorPluginIgnore.new(@config, @utils)
end end
@@ -16,6 +16,7 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
@config.expect :callback_include_count, true @config.expect :callback_include_count, true
@config.expect :callback_after_arg_check, false @config.expect :callback_after_arg_check, false
@config.expect :plugins, [:ignore] @config.expect :plugins, [:ignore]
@config.expect :debug_output, false
@cmock_generator_plugin_callback = CMockGeneratorPluginCallback.new(@config, @utils) @cmock_generator_plugin_callback = CMockGeneratorPluginCallback.new(@config, @utils)
end end
@@ -45,7 +46,8 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
expected = [ "typedef void (* CMOCK_Maple_CALLBACK)(int cmock_num_calls);\n", expected = [ "typedef void (* CMOCK_Maple_CALLBACK)(int cmock_num_calls);\n",
"void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n",
"void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n",
"#define Maple_StubWithCallback Maple_Stub\n" ].join "#define Maple_StubWithCallback Maple_Stub\n",
"int Maple_CallCount(void);\n" ].join
returned = @cmock_generator_plugin_callback.mock_function_declarations(function) returned = @cmock_generator_plugin_callback.mock_function_declarations(function)
assert_equal(expected, returned) assert_equal(expected, returned)
end end
@@ -55,7 +57,8 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
expected = [ "typedef void (* CMOCK_Maple_CALLBACK)(void);\n", expected = [ "typedef void (* CMOCK_Maple_CALLBACK)(void);\n",
"void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n",
"void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n",
"#define Maple_StubWithCallback Maple_Stub\n" ].join "#define Maple_StubWithCallback Maple_Stub\n",
"int Maple_CallCount(void);\n" ].join
@cmock_generator_plugin_callback.include_count = false @cmock_generator_plugin_callback.include_count = false
returned = @cmock_generator_plugin_callback.mock_function_declarations(function) returned = @cmock_generator_plugin_callback.mock_function_declarations(function)
assert_equal(expected, returned) assert_equal(expected, returned)
@@ -66,7 +69,8 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
expected = [ "typedef void (* CMOCK_Maple_CALLBACK)(int* tofu, int cmock_num_calls);\n", expected = [ "typedef void (* CMOCK_Maple_CALLBACK)(int* tofu, int cmock_num_calls);\n",
"void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n",
"void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n",
"#define Maple_StubWithCallback Maple_Stub\n" ].join "#define Maple_StubWithCallback Maple_Stub\n",
"int Maple_CallCount(void);\n" ].join
returned = @cmock_generator_plugin_callback.mock_function_declarations(function) returned = @cmock_generator_plugin_callback.mock_function_declarations(function)
assert_equal(expected, returned) assert_equal(expected, returned)
end end
@@ -76,7 +80,8 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
expected = [ "typedef const char* (* CMOCK_Maple_CALLBACK)(int* tofu, int cmock_num_calls);\n", expected = [ "typedef const char* (* CMOCK_Maple_CALLBACK)(int* tofu, int cmock_num_calls);\n",
"void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n",
"void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n",
"#define Maple_StubWithCallback Maple_Stub\n" ].join "#define Maple_StubWithCallback Maple_Stub\n",
"int Maple_CallCount(void);\n" ].join
returned = @cmock_generator_plugin_callback.mock_function_declarations(function) returned = @cmock_generator_plugin_callback.mock_function_declarations(function)
assert_equal(expected, returned) assert_equal(expected, returned)
end end
@@ -86,7 +91,8 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
expected = [ "typedef const char* (* CMOCK_Maple_CALLBACK)(int* tofu);\n", expected = [ "typedef const char* (* CMOCK_Maple_CALLBACK)(int* tofu);\n",
"void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_AddCallback(CMOCK_Maple_CALLBACK Callback);\n",
"void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n", "void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n",
"#define Maple_StubWithCallback Maple_Stub\n" ].join "#define Maple_StubWithCallback Maple_Stub\n",
"int Maple_CallCount(void);\n" ].join
@cmock_generator_plugin_callback.include_count = false @cmock_generator_plugin_callback.include_count = false
returned = @cmock_generator_plugin_callback.mock_function_declarations(function) returned = @cmock_generator_plugin_callback.mock_function_declarations(function)
assert_equal(expected, returned) assert_equal(expected, returned)
@@ -273,12 +279,18 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
"{\n", "{\n",
" Mock.Lemon_IgnoreBool = (char)0;\n", " Mock.Lemon_IgnoreBool = (char)0;\n",
" Mock.Lemon_CallbackBool = (char)1;\n", " Mock.Lemon_CallbackBool = (char)1;\n",
" Mock.Lemon_CallbackCalls = 0;\n",
" Mock.Lemon_CallbackFunctionPointer = Callback;\n", " Mock.Lemon_CallbackFunctionPointer = Callback;\n",
"}\n\n", "}\n\n",
"int Lemon_CallCount(void)\n",
"{\n",
" return Mock.Lemon_CallbackCalls;\n",
"}\n\n",
"void Lemon_Stub(CMOCK_Lemon_CALLBACK Callback)\n", "void Lemon_Stub(CMOCK_Lemon_CALLBACK Callback)\n",
"{\n", "{\n",
" Mock.Lemon_IgnoreBool = (char)0;\n", " Mock.Lemon_IgnoreBool = (char)0;\n",
" Mock.Lemon_CallbackBool = (char)0;\n", " Mock.Lemon_CallbackBool = (char)0;\n",
" Mock.Lemon_CallbackCalls = 0;\n",
" Mock.Lemon_CallbackFunctionPointer = Callback;\n", " Mock.Lemon_CallbackFunctionPointer = Callback;\n",
"}\n\n" "}\n\n"
].join ].join
@@ -18,7 +18,8 @@ describe CMockGeneratorPluginExpect, "Verify CMockGeneratorPluginExpect Module W
:enforce_strict_ordering => false, :enforce_strict_ordering => false,
:respond_to? => true, :respond_to? => true,
:create_error_stubs => true, :create_error_stubs => true,
:plugins => [ :expect ] ) :plugins => [ :expect ],
:debug_output => false )
@utils.expect :helpers, {} @utils.expect :helpers, {}
@cmock_generator_plugin_expect = CMockGeneratorPluginExpect.new(@config, @utils) @cmock_generator_plugin_expect = CMockGeneratorPluginExpect.new(@config, @utils)
@@ -12,7 +12,7 @@ describe CMockGeneratorPluginExpectAnyArgs, "Verify CMockGeneratorPluginExpectAn
before do before do
create_mocks :config, :utils create_mocks :config, :utils
@config = create_stub(:respond_to? => true, :create_error_stubs => true) @config = create_stub(:respond_to? => true, :create_error_stubs => true, :debug_output => false)
@cmock_generator_plugin_expect_any_args = CMockGeneratorPluginExpectAnyArgs.new(@config, @utils) @cmock_generator_plugin_expect_any_args = CMockGeneratorPluginExpectAnyArgs.new(@config, @utils)
end end
@@ -18,7 +18,8 @@ describe CMockGeneratorPluginExpect, "Verify CMockGeneratorPluginExpect Module w
:enforce_strict_ordering => true, :enforce_strict_ordering => true,
:respond_to? => true, :respond_to? => true,
:create_error_stubs => true, :create_error_stubs => true,
:plugins => [ :expect, :expect_any_args ] ) :plugins => [ :expect, :expect_any_args ],
:debug_output => false )
@utils.expect :helpers, {} @utils.expect :helpers, {}
@cmock_generator_plugin_expect = CMockGeneratorPluginExpect.new(@config, @utils) @cmock_generator_plugin_expect = CMockGeneratorPluginExpect.new(@config, @utils)
@@ -35,6 +35,7 @@ describe CMockGeneratorPluginIgnoreArg, "Verify CMockGeneratorPluginIgnoreArg Mo
:contains_ptr? => true } :contains_ptr? => true }
#no strict ordering #no strict ordering
@config.expect :debug_output, false
@cmock_generator_plugin_ignore_arg = CMockGeneratorPluginIgnoreArg.new(@config, @utils) @cmock_generator_plugin_ignore_arg = CMockGeneratorPluginIgnoreArg.new(@config, @utils)
end end
@@ -12,7 +12,7 @@ describe CMockGeneratorPluginIgnoreStateless, "Verify CMockGeneratorPluginIgnore
before do before do
create_mocks :config, :utils create_mocks :config, :utils
@config = create_stub(:respond_to? => true, :create_error_stubs => true) @config = create_stub(:respond_to? => true, :create_error_stubs => true, :debug_output => false)
@cmock_generator_plugin_ignore_stateless = CMockGeneratorPluginIgnoreStateless.new(@config, @utils) @cmock_generator_plugin_ignore_stateless = CMockGeneratorPluginIgnoreStateless.new(@config, @utils)
end end
@@ -12,7 +12,7 @@ describe CMockGeneratorPluginIgnore, "Verify CMockGeneratorPluginIgnore Module"
before do before do
create_mocks :config, :utils create_mocks :config, :utils
@config = create_stub(:respond_to? => true, :create_error_stubs => true) @config = create_stub(:respond_to? => true, :create_error_stubs => true, :debug_output => false)
@cmock_generator_plugin_ignore = CMockGeneratorPluginIgnore.new(@config, @utils) @cmock_generator_plugin_ignore = CMockGeneratorPluginIgnore.new(@config, @utils)
end end
@@ -56,8 +56,20 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh
:return => test_return[:void], :return => test_return[:void],
:contains_ptr? => true } :contains_ptr? => true }
# void Cedar(volatile struct foo_obj *foo_handle)
# arg[:type] has volatile stripped at parse time; volatile? flag carries the information
@volatile_ptr_func = {:name => "Cedar",
:args => [{ :type => "struct foo_obj*",
:name => "foo_handle",
:ptr? => true,
:volatile? => true,
}],
:return => test_return[:void],
:contains_ptr? => true }
#no strict ordering #no strict ordering
@config.expect :plugins, [] @config.expect :plugins, []
@config.expect :debug_output, false
@cmock_generator_plugin_return_thru_ptr = CMockGeneratorPluginReturnThruPtr.new(@config, @utils) @cmock_generator_plugin_return_thru_ptr = CMockGeneratorPluginReturnThruPtr.new(@config, @utils)
end end
@@ -82,6 +94,10 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh
@config.expect :treat_as_void, ['MY_FANCY_VOID'] @config.expect :treat_as_void, ['MY_FANCY_VOID']
end end
def volatile_ptr_func_expect
@utils.expect :ptr_or_str?, true, ['struct foo_obj*']
end
it "have set up internal priority correctly on init" do it "have set up internal priority correctly on init" do
assert_equal(9, @cmock_generator_plugin_return_thru_ptr.priority) assert_equal(9, @cmock_generator_plugin_return_thru_ptr.priority)
end end
@@ -207,6 +223,51 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh
assert_equal(expected, returned) assert_equal(expected, returned)
end end
it "has no volatile in the Val typedef member for a volatile pointer arg (type is pre-stripped)" do
volatile_ptr_func_expect()
# arg[:type] = "struct foo_obj*" (volatile stripped at parse time)
# ptr_to_const("struct foo_obj*") => "struct foo_obj const*"
expected = " char ReturnThruPtr_foo_handle_Used;\n" +
" struct foo_obj const* ReturnThruPtr_foo_handle_Val;\n" +
" size_t ReturnThruPtr_foo_handle_Size;\n"
returned = @cmock_generator_plugin_return_thru_ptr.instance_typedefs(@volatile_ptr_func)
assert_equal(expected, returned)
end
it "has no volatile in the _CMockReturnMemThruPtr_ declaration for a volatile pointer arg" do
volatile_ptr_func_expect()
# arg[:type] = "struct foo_obj*" (volatile stripped), so sizeof and param type are clean.
expected =
"#define Cedar_ReturnThruPtr_foo_handle(foo_handle)" +
" Cedar_CMockReturnMemThruPtr_foo_handle(__LINE__, foo_handle, sizeof(struct foo_obj))\n" +
"#define Cedar_ReturnArrayThruPtr_foo_handle(foo_handle, cmock_len)" +
" Cedar_CMockReturnMemThruPtr_foo_handle(__LINE__, foo_handle, (cmock_len * sizeof(*foo_handle)))\n" +
"#define Cedar_ReturnMemThruPtr_foo_handle(foo_handle, cmock_size)" +
" Cedar_CMockReturnMemThruPtr_foo_handle(__LINE__, foo_handle, (cmock_size))\n" +
"void Cedar_CMockReturnMemThruPtr_foo_handle(UNITY_LINE_TYPE cmock_line, struct foo_obj const* foo_handle, size_t cmock_size);\n"
returned = @cmock_generator_plugin_return_thru_ptr.mock_function_declarations(@volatile_ptr_func)
assert_equal(expected, returned)
end
it "uses (void*) cast in mock_implementation for volatile pointer arg" do
volatile_ptr_func_expect()
expected =
" if (cmock_call_instance->ReturnThruPtr_foo_handle_Used)\n" +
" {\n" +
" UNITY_TEST_ASSERT_NOT_NULL(foo_handle, cmock_line, CMockStringPtrIsNULL);\n" +
" CMOCK_MEMCPY((void*)foo_handle, (const void*)cmock_call_instance->ReturnThruPtr_foo_handle_Val,\n" +
" cmock_call_instance->ReturnThruPtr_foo_handle_Size);\n" +
" }\n"
returned = @cmock_generator_plugin_return_thru_ptr.mock_implementation(@volatile_ptr_func).join("")
assert_equal(expected, returned)
end
it "converts single pointer type to pointer-to-const via ptr_to_const" do it "converts single pointer type to pointer-to-const via ptr_to_const" do
plugin = @cmock_generator_plugin_return_thru_ptr plugin = @cmock_generator_plugin_return_thru_ptr
assert_equal("int const*", plugin.ptr_to_const("int*")) assert_equal("int const*", plugin.ptr_to_const("int*"))
+231
View File
@@ -27,6 +27,7 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
@config.expect :inline_function_patterns, ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*'] @config.expect :inline_function_patterns, ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*']
@config.expect :array_size_type, ['int', 'size_t'] @config.expect :array_size_type, ['int', 'size_t']
@config.expect :array_size_name, 'size|len' @config.expect :array_size_name, 'size|len'
@config.expect :ct_assert_patterns, ['ct_assert', '_?[Ss]tatic_[Aa]ssert', '_Static_assert', 'STATIC_ASSERT', 'BUILD_ASSERT', 'CTASSERT']
@parser = CMockHeaderParser.new(@config) @parser = CMockHeaderParser.new(@config)
@@ -146,6 +147,118 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
assert_equal(expected, @parser.import_source(source, @test_project)) assert_equal(expected, @parser.import_source(source, @test_project))
end end
it "remove code inside #if 0 blocks" do
source =
"void before(void);\n" +
"#if 0\n" +
"void hidden(void);\n" +
"#endif\n" +
"void after(void);\n"
expected = ["void before(void)", "void after(void)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "keep code inside #if 1 blocks" do
source =
"void before(void);\n" +
"#if 1\n" +
"void visible(void);\n" +
"#endif\n" +
"void after(void);\n"
expected = ["void before(void)", "void visible(void)", "void after(void)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "keep else branch of #if 0 blocks" do
source =
"void before(void);\n" +
"#if 0\n" +
"void hidden(void);\n" +
"#else\n" +
"void visible(void);\n" +
"#endif\n" +
"void after(void);\n"
expected = ["void before(void)", "void visible(void)", "void after(void)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "remove else branch of #if 1 blocks" do
source =
"void before(void);\n" +
"#if 1\n" +
"void visible(void);\n" +
"#else\n" +
"void hidden(void);\n" +
"#endif\n" +
"void after(void);\n"
expected = ["void before(void)", "void visible(void)", "void after(void)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "handle nested #if 1 blocks inside #if 0" do
source =
"void before(void);\n" +
"#if 0\n" +
"void hidden1(void);\n" +
"#if 1\n" +
"void hidden2(void);\n" +
"#endif\n" +
"void hidden3(void);\n" +
"#endif\n" +
"void after(void);\n"
expected = ["void before(void)", "void after(void)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "handle nested #if 0 blocks inside #if 1" do
source =
"void before(void);\n" +
"#if 1\n" +
"void visible1(void);\n" +
"#if 0\n" +
"void hidden1(void);\n" +
"#endif\n" +
"void visible2(void);\n" +
"#endif\n" +
"void after(void);\n"
expected = ["void before(void)", "void visible1(void)", "void visible2(void)", "void after(void)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "handle back to back #if 1 blocks and #if 0 blocks" do
source =
"void before(void);\n" +
"#if 0\n" +
"void hidden1(void);\n" +
"#endif\n" +
"#if 1\n" +
"void visible1(void);\n" +
"#endif\n" +
"#if 0\n" +
"void hidden2(void);\n" +
"#endif\n" +
"#if 1\n" +
"void visible2(void);\n" +
"#endif\n" +
"void after(void);\n"
expected = ["void before(void)", "void visible1(void)", "void visible2(void)", "void after(void)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "remove assembler pragma sections" do it "remove assembler pragma sections" do
source = source =
@@ -259,6 +372,32 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
end end
it "ignore compile-time assertions and not treat them as function prototypes" do
source =
"static_assert(BLAH_BOOLEAN_TRAIT);\n" +
"void real_func(int a);\n" +
"ct_assert(COMMIT_ID_H, ID_SIZE == (sizeof(ID)) - 1);\n" +
"CTASSERT(OTHER_HEADER_H, BUF_SIZE != 0);\n" +
"BUILD_ASSERT(MAX_LEN <= 256);\n" +
"STATIC_ASSERT(sizeof(MyStruct) >= 4);\n"
expected = ["void real_func(int a)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "ignore function pointer variables with pointer return types and not treat them as function prototypes" do
source =
"struct_t * (*func)(some_argument);\n" +
"void real_func(int a);\n"
expected = ["void real_func(int a)"]
assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip })
end
it "remove struct statements" do it "remove struct statements" do
source = source =
"struct _NamedStruct1 {\n" + "struct _NamedStruct1 {\n" +
@@ -1119,6 +1258,98 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
assert_equal(expected, @parser.parse("module", source)[:functions]) assert_equal(expected, @parser.parse("module", source)[:functions])
end end
it "properly parses volatile pointer argument types" do
source = "int16_t foo(volatile struct foo_obj *foo_handle, int plain, volatile int not_a_ptr);\n"
expected = [{ :name => "foo",
:unscoped_name => "foo",
:namespace=>[],
:class=>nil,
:modifier => "",
:return => { :type => "int16_t",
:name => "cmock_to_return",
:str => "int16_t cmock_to_return",
:void? => false,
:ptr? => false,
:const? => false,
:const_ptr? => false
},
:var_arg => nil,
:args_string => "volatile struct foo_obj* foo_handle, int plain, volatile int not_a_ptr",
:args => [{ :type => "struct foo_obj*", :name => "foo_handle",
:ptr? => true, :string? => false, :const? => false, :const_ptr? => false, :volatile? => true },
{ :type => "int", :name => "plain",
:ptr? => false, :string? => false, :const? => false, :const_ptr? => false },
{ :type => "volatile int", :name => "not_a_ptr",
:ptr? => false, :string? => false, :const? => false, :const_ptr? => false }],
:args_call => "foo_handle, plain, not_a_ptr",
:contains_ptr? => true
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "properly parses T volatile* style volatile pointer argument types" do
# volatile can appear before OR after the base type; both mean "pointer to volatile T"
# e.g. "int volatile*" is identical to "volatile int*" in C
source = "void bar(int volatile* a, struct foo_obj volatile* b);\n"
expected = [{ :name => "bar",
:unscoped_name => "bar",
:namespace=>[],
:class=>nil,
:modifier => "",
:return => { :type => "void",
:name => "cmock_to_return",
:str => "void cmock_to_return",
:void? => true,
:ptr? => false,
:const? => false,
:const_ptr? => false
},
:var_arg => nil,
:args_string => "int volatile* a, struct foo_obj volatile* b",
:args => [{ :type => "int*", :name => "a",
:ptr? => true, :string? => false, :const? => false, :const_ptr? => false, :volatile? => true },
{ :type => "struct foo_obj*", :name => "b",
:ptr? => true, :string? => false, :const? => false, :const_ptr? => false, :volatile? => true }],
:args_call => "a, b",
:contains_ptr? => true
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "treats int*volatile (volatile pointer to non-volatile type) as a plain pointer without volatile? flag" do
# "int * volatile p" = volatile pointer to int (the pointer itself is volatile, not the pointed-to value)
# This is distinct from "volatile int *p" (pointer to volatile int).
# CMock must NOT set volatile? here: the pointed-to type is not volatile, so no cast-qual issue.
source = "void baz(int * volatile p);\n"
expected = [{ :name => "baz",
:unscoped_name => "baz",
:namespace=>[],
:class=>nil,
:modifier => "",
:return => { :type => "void",
:name => "cmock_to_return",
:str => "void cmock_to_return",
:void? => true,
:ptr? => false,
:const? => false,
:const_ptr? => false
},
:var_arg => nil,
:args_string => "int* volatile p",
:args => [{ :type => "int* volatile", :name => "p",
:ptr? => true, :string? => false, :const? => false, :const_ptr? => false }],
:args_call => "p",
:contains_ptr? => true
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "converts typedef'd array arguments to pointers" do it "converts typedef'd array arguments to pointers" do
source = "Book AddToBook(Book book, const IntArray values);\n" source = "Book AddToBook(Book book, const IntArray values);\n"
+2 -1
View File
@@ -21,7 +21,8 @@ describe CMockPluginManager, "Verify CMockPluginManager Module" do
:enforce_strict_ordering => false, :enforce_strict_ordering => false,
:ignore => :args_and_calls, :ignore => :args_and_calls,
:exclude_setjmp_h => false, :exclude_setjmp_h => false,
:create_error_stubs => true :create_error_stubs => true,
:debug_output => false
) )
def @config.plugins def @config.plugins