mirror of
https://github.com/ThrowTheSwitch/CMock.git
synced 2026-09-03 08:50:00 +00:00
Merge pull request #535 from ThrowTheSwitch/refactor/actions
Implement broader testing across platforms.
This commit is contained in:
+144
-25
@@ -11,42 +11,161 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
# Job: Unit test suite
|
||||
unit-tests:
|
||||
name: "Unit Tests"
|
||||
runs-on: ubuntu-latest
|
||||
# Job: Ruby unit tests across all supported Ruby versions and OSes.
|
||||
# These are fast (no C compilation) and verify the generator logic is Ruby-version-portable.
|
||||
ruby-tests:
|
||||
name: "Ruby Tests (${{ matrix.os }}, Ruby ${{ matrix.ruby }})"
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
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:
|
||||
# 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
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# Setup Ruby Testing Tools to do tests on multiple ruby version
|
||||
- name: Setup Ruby Testing Tools
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
|
||||
# Install Ruby Testing Tools
|
||||
- name: Setup Ruby Testing Tools
|
||||
- name: Install Ruby Dependencies
|
||||
run: |
|
||||
sudo gem install rspec
|
||||
sudo gem install rubocop -v 1.57.2
|
||||
sudo gem install bundler
|
||||
bundle update
|
||||
bundle install
|
||||
gem install bundler
|
||||
bundle install
|
||||
|
||||
# Run Tests
|
||||
- name: Run All Unit Tests
|
||||
- name: Run Ruby Unit Tests
|
||||
env:
|
||||
NO_COLOR: ${{ matrix.os == 'windows-latest' && '1' || '' }}
|
||||
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
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
source "http://rubygems.org/"
|
||||
source "https://rubygems.org/"
|
||||
|
||||
gem 'rspec'
|
||||
gem 'rubocop', '1.57.2'
|
||||
|
||||
+23
-3
@@ -189,7 +189,7 @@ care how many times it was called, right?
|
||||
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
|
||||
previously called Ignore or IgnoreAndReturn requiring you to Expect or otherwise
|
||||
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(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:
|
||||
----------------
|
||||
|
||||
@@ -951,8 +970,9 @@ that exposes them as virtual methods and modify your code to inject mocks at
|
||||
run-time... but there is another way!
|
||||
|
||||
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++
|
||||
mocking framework together in the same test!)
|
||||
to handle the virtual methods. CMock does NOT mock non-static members. For those,
|
||||
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
|
||||
unit test too, we need to resolve multiple definition errors with something like
|
||||
|
||||
@@ -80,12 +80,11 @@ class CMockGeneratorPluginReturnThruPtr
|
||||
arg_name = arg[:name]
|
||||
next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
|
||||
|
||||
dest_cast = arg[:volatile?] ? '(void*)(CMOCK_MEM_PTR_AS_INT)' : '(void*)'
|
||||
lines << " if (Mock.#{function[:name]}_IgnoreBool && cmock_call_instance != NULL &&\n"
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n"
|
||||
lines << " {\n"
|
||||
lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n"
|
||||
lines << " CMOCK_MEMCPY(#{dest_cast}#{arg_name}, (const void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n"
|
||||
lines << " CMOCK_MEMCPY((void*)#{arg_name}, (const void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n"
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n"
|
||||
lines << " }\n"
|
||||
end
|
||||
@@ -136,11 +135,10 @@ class CMockGeneratorPluginReturnThruPtr
|
||||
arg_name = arg[:name]
|
||||
next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
|
||||
|
||||
dest_cast = arg[:volatile?] ? '(void*)(CMOCK_MEM_PTR_AS_INT)' : '(void*)'
|
||||
lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n"
|
||||
lines << " {\n"
|
||||
lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n"
|
||||
lines << " CMOCK_MEMCPY(#{dest_cast}#{arg_name}, (const void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n"
|
||||
lines << " CMOCK_MEMCPY((void*)#{arg_name}, (const void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n"
|
||||
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n"
|
||||
lines << " }\n"
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ class CMockHeaderParser
|
||||
@c_calling_conventions = cfg.c_calling_conventions.uniq
|
||||
@treat_as_array = cfg.treat_as_array
|
||||
@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
|
||||
@standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq
|
||||
@array_size_name = cfg.array_size_name
|
||||
@@ -118,15 +118,13 @@ class CMockHeaderParser
|
||||
|
||||
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)
|
||||
if RUBY_VERSION.split('.')[0].to_i > 1
|
||||
# we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash.
|
||||
r = '\\{([^\\{\\}]*|\\g<0>)*\\}'
|
||||
source.gsub!(/#{r}/m, '{ }')
|
||||
else
|
||||
while source.gsub!(/\{[^{}]*\{[^{}]*\}[^{}]*\}/m, '{ }')
|
||||
end
|
||||
# Collapse innermost brace pairs first using a brace-free sentinel (\x00), working
|
||||
# outward until no balanced pairs remain. This avoids the catastrophic backtracking
|
||||
# of the recursive regex \{([^\{\}]*|\g<0>)*\} on Ruby < 3.2 while preserving
|
||||
# identical semantics: every balanced brace structure is collapsed to '{ }'.
|
||||
while source.gsub!(/\{[^{}]*\}/m, "\x00")
|
||||
end
|
||||
|
||||
source.gsub!("\x00", '{ }')
|
||||
source
|
||||
end
|
||||
|
||||
@@ -311,7 +309,7 @@ class CMockHeaderParser
|
||||
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!(/\b\w+\s*\((?:[^()!=<>]|\([^()]*\))*(?:==|!=|<=|>=)[^;]*\)/, '')
|
||||
|
||||
source.gsub!(/\s*=\s*['"a-zA-Z0-9_.]+\s*/, '') # remove default value statements from argument lists
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -80,7 +80,7 @@ module RakefileHelpers
|
||||
raise "Cannot find Config File #{config_target}"
|
||||
end
|
||||
|
||||
$colour_output = $proj[:project][:colour]
|
||||
$colour_output = $proj[:project][:colour] && !ENV['NO_COLOR']
|
||||
end
|
||||
|
||||
def configure_clean
|
||||
|
||||
@@ -253,14 +253,14 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh
|
||||
assert_equal(expected, returned)
|
||||
end
|
||||
|
||||
it "uses (void*)(CMOCK_MEM_PTR_AS_INT) cast in mock_implementation for volatile pointer arg" do
|
||||
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*)(CMOCK_MEM_PTR_AS_INT)foo_handle, (const void*)cmock_call_instance->ReturnThruPtr_foo_handle_Val,\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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user