update ceedling to 0.31.1

This commit is contained in:
hathach 2022-12-08 09:14:38 +07:00
parent d9817ebe17
commit a652212f27
98 changed files with 2703 additions and 6609 deletions

View File

@ -15,6 +15,7 @@
# :release_build: TRUE # :release_build: TRUE
:test_file_prefix: test_ :test_file_prefix: test_
:which_ceedling: vendor/ceedling :which_ceedling: vendor/ceedling
:ceedling_version: 0.31.1
:default_tasks: :default_tasks:
- test:all - test:all

View File

@ -51,7 +51,7 @@
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build // CFG_TUSB_DEBUG is defined by compiler in DEBUG build
#ifndef CFG_TUSB_DEBUG #ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0 #define CFG_TUSB_DEBUG 1
#endif #endif
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.

View File

@ -49,16 +49,16 @@ unless (project_found)
end end
desc "upgrade PROJECT_NAME", "upgrade ceedling for a project (not req'd if gem used)" desc "upgrade PROJECT_NAME", "upgrade ceedling for a project (not req'd if gem used)"
method_option :docs, :type => :boolean, :default => false, :desc => "Add docs in project vendor directory"
method_option :local, :type => :boolean, :default => false, :desc => "Create a copy of Ceedling in the project vendor directory"
method_option :no_configs, :type => :boolean, :default => false, :desc => "Don't install starter configuration files"
method_option :noconfigs, :type => :boolean, :default => false
#deprecated:
method_option :no_docs, :type => :boolean, :default => false
method_option :nodocs, :type => :boolean, :default => false
def upgrade(name, silent = false) def upgrade(name, silent = false)
copy_assets_and_create_structure(name, silent, true, options || {:upgrade => true}) as_local = true
begin
require "yaml"
as_local = (YAML.load_file(File.join(name, "project.yml"))[:project][:which_ceedling] != 'gem')
rescue
raise "ERROR: Could not find valid project file '#{yaml_path}'"
end
found_docs = File.exists?( File.join(name, "docs", "CeedlingPacket.md") )
copy_assets_and_create_structure(name, silent, true, {:upgrade => true, :no_configs => true, :local => as_local, :docs => found_docs})
end end
no_commands do no_commands do
@ -90,26 +90,30 @@ unless (project_found)
FileUtils.touch(File.join(test_support_path, '.gitkeep')) FileUtils.touch(File.join(test_support_path, '.gitkeep'))
# If documentation requested, create a place to dump them and do so # If documentation requested, create a place to dump them and do so
doc_path = ""
if use_docs if use_docs
doc_path = File.join(ceedling_path, 'docs') doc_path = use_gem ? File.join(name, 'docs') : File.join(ceedling_path, 'docs')
FileUtils.mkdir_p doc_path FileUtils.mkdir_p doc_path
in_doc_path = lambda {|f| File.join(doc_path, f)} in_doc_path = lambda {|f| File.join(doc_path, f)}
doc_files = [ # Add documentation from main projects to list
'docs/CeedlingPacket.md', doc_files = {}
'vendor/c_exception/docs/CException.md', ['docs','vendor/unity/docs','vendor/cmock/docs','vendor/cexception/docs'].each do |p|
'vendor/cmock/docs/CMock_Summary.md', Dir[ File.expand_path(File.join(here, p, '*.md')) ].each do |f|
'vendor/unity/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf', doc_files[ File.basename(f) ] = f unless(doc_files.include? f)
'vendor/unity/docs/UnityAssertionsReference.md', end
'vendor/unity/docs/UnityConfigurationGuide.md', end
'vendor/unity/docs/UnityGettingStartedGuide.md',
'vendor/unity/docs/UnityHelperScriptsGuide.md',
'vendor/unity/docs/ThrowTheSwitchCodingStandard.md',
]
doc_files.each do |f| # Add documentation from plugins to list
copy_file(f, in_doc_path.call(File.basename(f)), :force => force) Dir[ File.join(here, 'plugins/**/README.md') ].each do |plugin_path|
k = "plugin_" + plugin_path.split(/\\|\//)[-2] + ".md"
doc_files[ k ] = File.expand_path(plugin_path)
end
# Copy all documentation
doc_files.each_pair do |k, v|
copy_file(v, in_doc_path.call(k), :force => force)
end end
end end
@ -133,7 +137,6 @@ unless (project_found)
{:src => 'vendor/cmock/config/', :dst => 'vendor/cmock/config'}, {:src => 'vendor/cmock/config/', :dst => 'vendor/cmock/config'},
{:src => 'vendor/cmock/lib/', :dst => 'vendor/cmock/lib'}, {:src => 'vendor/cmock/lib/', :dst => 'vendor/cmock/lib'},
{:src => 'vendor/cmock/src/', :dst => 'vendor/cmock/src'}, {:src => 'vendor/cmock/src/', :dst => 'vendor/cmock/src'},
{:src => 'vendor/deep_merge/lib/', :dst => 'vendor/deep_merge/lib'},
{:src => 'vendor/diy/lib', :dst => 'vendor/diy/lib'}, {:src => 'vendor/diy/lib', :dst => 'vendor/diy/lib'},
{:src => 'vendor/unity/auto/', :dst => 'vendor/unity/auto'}, {:src => 'vendor/unity/auto/', :dst => 'vendor/unity/auto'},
{:src => 'vendor/unity/src/', :dst => 'vendor/unity/src'}, {:src => 'vendor/unity/src/', :dst => 'vendor/unity/src'},
@ -146,16 +149,24 @@ unless (project_found)
# We're copying in a configuration file if we haven't said not to # We're copying in a configuration file if we haven't said not to
if (use_configs) if (use_configs)
if use_gem dst_yaml = File.join(name, 'project.yml')
copy_file(File.join('assets', 'project_as_gem.yml'), File.join(name, 'project.yml'), :force => force) src_yaml = if use_gem
File.join(here, 'assets', 'project_as_gem.yml')
else else
copy_file(File.join('assets', 'project_with_guts.yml'), File.join(name, 'project.yml'), :force => force)
if is_windows? if is_windows?
copy_file(File.join('assets', 'ceedling.cmd'), File.join(name, 'ceedling.cmd'), :force => force) copy_file(File.join('assets', 'ceedling.cmd'), File.join(name, 'ceedling.cmd'), :force => force)
else else
copy_file(File.join('assets', 'ceedling'), File.join(name, 'ceedling'), :force => force) copy_file(File.join('assets', 'ceedling'), File.join(name, 'ceedling'), :force => force)
File.chmod(0755, File.join(name, 'ceedling')) File.chmod(0755, File.join(name, 'ceedling'))
end end
File.join(here, 'assets', 'project_with_guts.yml')
end
# Perform the actual clone of the config file, while updating the version
File.open(dst_yaml,'w') do |dst|
require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb"))
dst << File.read(src_yaml).gsub(":ceedling_version: '?'",":ceedling_version: #{Ceedling::Version::CEEDLING}")
puts " create #{dst_yaml}"
end end
end end
@ -167,8 +178,8 @@ unless (project_found)
unless silent unless silent
puts "\n" puts "\n"
puts "Project '#{name}' #{force ? "upgraded" : "created"}!" puts "Project '#{name}' #{force ? "upgraded" : "created"}!"
puts " - Tool documentation is located in vendor/ceedling/docs" if use_docs puts " - Tool documentation is located in #{doc_path}" if use_docs
puts " - Execute 'ceedling help' to view available test & build tasks" puts " - Execute 'ceedling help' from #{name} to view available test & build tasks"
puts '' puts ''
end end
end end
@ -206,10 +217,10 @@ unless (project_found)
desc "version", "return the version of the tools installed" desc "version", "return the version of the tools installed"
def version() def version()
require 'ceedling/version.rb' require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb"))
puts " Ceedling:: #{Ceedling::Version::CEEDLING}" puts " Ceedling:: #{Ceedling::Version::CEEDLING}"
puts " CMock:: #{Ceedling::Version::CMOCK}" puts " CMock:: #{Ceedling::Version::CMOCK}"
puts " Unity:: #{Ceedling::Version::UNITY}" puts " Unity:: #{Ceedling::Version::UNITY}"
puts " CException:: #{Ceedling::Version::CEXCEPTION}" puts " CException:: #{Ceedling::Version::CEXCEPTION}"
end end
end end
@ -287,6 +298,8 @@ else
options[:list_tasks] = true options[:list_tasks] = true
when /^-T$/ when /^-T$/
options[:list_tasks] = true options[:list_tasks] = true
when /^--tasks$/
options[:list_tasks] = true
when /^project:(\w+)/ when /^project:(\w+)/
ENV['CEEDLING_USER_PROJECT_FILE'] = "#{$1}.yml" ENV['CEEDLING_USER_PROJECT_FILE'] = "#{$1}.yml"
else else

View File

@ -1,292 +0,0 @@
CException
==========
CException is a basic exception framework for C, suitable for use in
embedded applications. It provides an exception framework similar in
use to C++, but with much less overhead.
CException uses C standard library functions `setjmp` and `longjmp` to
operate. As long as the target system has these two functions defined,
this library should be useable with very little configuration. It
even supports environments where multiple program flows are in use,
such as real-time operating systems.
There are about a gabillion exception frameworks using a similar
setjmp/longjmp method out there... and there will probably be more
in the future. Unfortunately, when we started our last embedded
project, all those that existed either (a) did not support multiple
tasks (therefore multiple stacks) or (b) were way more complex than
we really wanted. CException was born.
*Why use CException?*
0. It's ANSI C, and it beats passing error codes around.
1. You want something simple... CException throws a single id. You can
define those ID's to be whatever you like. You might even choose which
type that number is for your project. But that's as far as it goes.
We weren't interested in passing objects or structs or strings...
just simple error codes.
2. Performance... CException can be configured for single tasking or
multitasking. In single tasking, there is very little overhead past
the setjmp/longjmp calls (which are already fast). In multitasking,
your only additional overhead is the time it takes you to determine
a unique task id 0 - num_tasks.
For the latest version, go to [ThrowTheSwitch.org](http://throwtheswitch.org)
CONTENTS OF THIS DOCUMENT
=========================
* Usage
* Limitations
*API
* Configuration
* Testing
* License
Usage
-----
Code that is to be protected are wrapped in `Try { } Catch { }` blocks.
The code directly following the Try call is "protected", meaning that
if any Throws occur, program control is directly transferred to the
start of the Catch block.
A numerical exception ID is included with Throw, and is made accessible
from the Catch block.
Throws can occur from within function calls (nested as deeply as you
like) or directly from within the function itself.
Limitations
-----------
This library was made to be as fast as possible, and provide basic
exception handling. It is not a full-blown exception library. Because
of this, there are a few limitations that should be observed in order
to successfully utilize this library:
1. Do not directly "return" from within a `Try` block, nor `goto`
into or out of a `Try` block.
*Why?*
The `Try` macro allocates some local memory and alters a global
pointer. These are cleaned up at the top of the `Catch` macro.
Gotos and returns would bypass some of these steps, resulting in
memory leaks or unpredictable behavior.
2. If (a) you change local (stack) variables within your `Try` block,
AND (b) wish to make use of the updated values after an exception
is thrown, those variables should be made `volatile`. Note that this
is ONLY for locals and ONLY when you need access to them after a
`Throw`.
*Why?*
Compilers optimize. There is no way to guarantee that the actual
memory location was updated and not just a register unless the
variable is marked volatile.
3. Memory which is `malloc`'d or `new`'d is not automatically released
when an error is thrown. This will sometimes be desirable, and
othertimes may not. It will be the responsibility of the `Catch`
block to perform this kind of cleanup.
*Why?*
There's just no easy way to track `malloc`'d memory, etc., without
replacing or wrapping malloc calls or something like that. This
is a light framework, so these options were not desirable.
API
---
###Try
`Try` is a macro which starts a protected block. It MUST be followed by
a pair of braces or a single protected line (similar to an 'if'),
enclosing the data that is to be protected. It **must** be followed by a
`Catch` block (don't worry, you'll get compiler errors to let you know if
you mess any of that up).
###Catch(e)
`Catch` is a macro which ends the `Try` block and starts the error handling
block. The `Catch` block is called if and only if an exception was thrown
while within the `Try` block. This error was thrown by a `Throw` call
somewhere within `Try` (or within a function called within `Try`, or a function
called by a function called within `Try`, etc).
The single parameter `e` is filled with the error code which was thrown.
This can be used for reporting, conditional cleanup, etc. (or you can just
ignore it if you really want... people ignore return codes all the time,
right?). `e` should be of type `EXCEPTION_T`
###Throw(e)
This is the method of throwing an error. A `Throw` should only occur from within a
protected (`Try` ... `Catch`) block, though it may easily be nested many function
calls deep without an impact on performance or functionality. `Throw` takes
a single argument, which is an exception id which will be passed to `Catch`
as the reason for the error.
If you wish to rethrow an error, this can be done by calling `Throw(e)` with
the error code you just caught. It **is** valid to throw from a catch block.
###ExitTry()
On rare occasion, you might want to immediately exit your current `Try` block
but **not** treat this as an error. Don't run the `Catch`. Just start executing
from after the `Catch` as if nothing had happened... That's what `ExitTry` is
for.
CONFIGURATION
-------------
CException is a mostly portable library. It has one universal
dependency, and some macros which are required if working in a
multi-tasking environment.
1. The standard C library setjmp must be available. Since this is part
of the standard library, chances are good that you'll be fine.
2. If working in a multitasking environment, methods for obtaining an
index into an array of frames and to get the overall number of
id's are required. If the OS supports a method to retrieve Task
ID's, and those Tasks are number 0, 1, 2... you are in an ideal
situation. Otherwise, a more creative mapping function may be
required. Note that this function is likely to be called twice
for each protected block and once during a throw. This is the
only overhead in the system.
Exception.h
-----------
By convention, most projects include `Exception.h` which defines any
further requirements, then calls `CException.h` to do the gruntwork. All
of these are optional. You could directly include `CException.h` if
you wanted and just use the defaults provided.
* `EXCEPTION_T`
* Set this to the type you want your exception id's to be. Defaults to 'unsigned int'.
* `EXCEPTION_NONE`
* Set this to a number which will never be an exception id in your system. Defaults to `0x5a5a5a5a`.
* `EXCEPTION_GET_ID`
* If in a multi-tasking environment, this should be
set to be a call to the function described in #2 above.
Defaults to just return `0` all the time (good for
single tasking environments)
* `EXCEPTION_NUM_ID`
* If in a multi-tasking environment, this should be set
to the number of ID's required (usually the number of
tasks in the system). Defaults to `1` (for single
tasking environments).
* `CEXCEPTION_NO_CATCH_HANDLER(id)`
* This macro can be optionally specified.
It allows you to specify code to be called when a Throw
is made outside of `Try` ... `Catch` protection. Consider
this the emergency fallback plan for when something has
gone terribly wrong.
You may also want to include any header files which will commonly be
needed by the rest of your application where it uses exception handling
here. For example, OS header files or exception codes would be useful.
Finally, there are some hook macros which you can implement to inject
your own target-specific code in particular places. It is a rare instance
where you will need these, but they are here if you need them:
* `CEXCEPTION_HOOK_START_TRY`
* called immediately before the Try block
* `CEXCEPTION_HOOK_HAPPY_TRY`
* called immediately after the Try block if no exception was thrown
* `CEXCEPTION_HOOK_AFTER_TRY`
* called immediately after the Try block OR before an exception is caught
* `CEXCEPTION_HOOK_START_CATCH`
* called immediately before the catch
TESTING
-------
If you want to validate that CException works with your tools or that
it works with your custom configuration, you may want to run the test
suite.
The test suite included makes use of the `Unity` Test Framework. It will
require a native C compiler. The example makefile uses MinGW's gcc.
Modify the makefile to include the proper paths to tools, then run `make`
to compile and run the test application.
* `C_COMPILER`
* The C compiler to use to perform the tests
* `C_LIBS`
* The path to the C libraries (including setjmp)
* `UNITY_DIR`
* The path to the Unity framework (required to run tests)
(get it at [ThrowTheSwitch.org](http://throwtheswitch.org))
LICENSE
-------
This software is licensed under the MIT License
Copyright (c) 2007-2017 Mark VanderVoord
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,603 +0,0 @@
CMock: A Summary
================
*[ThrowTheSwitch.org](http://throwtheswitch.org)*
*This documentation is released under a Creative Commons 3.0 Attribution Share-Alike License*
What Exactly Are We Talking About Here?
---------------------------------------
CMock is a nice little tool which takes your header files and creates
a Mock interface for it so that you can more easily unit test modules
that touch other modules. For each function prototype in your
header, like this one:
int DoesSomething(int a, int b);
...you get an automatically generated DoesSomething function
that you can link to instead of your real DoesSomething function.
By using this Mocked version, you can then verify that it receives
the data you want, and make it return whatever data you desire,
make it throw errors when you want, and more... Create these for
everything your latest real module touches, and you're suddenly
in a position of power: You can control and verify every detail
of your latest creation.
To make that easier, CMock also gives you a bunch of functions
like the ones below, so you can tell that generated DoesSomething
function how to behave for each test:
void DoesSomething_ExpectAndReturn(int a, int b, int toReturn);
void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error);
void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback);
void DoesSomething_IgnoreAndReturn(int toReturn);
You can pile a bunch of these back to back, and it remembers what
you wanted to pass when, like so:
test_CallsDoesSomething_ShouldDoJustThat(void)
{
DoesSomething_ExpectAndReturn(1,2,3);
DoesSomething_ExpectAndReturn(4,5,6);
DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS);
CallsDoesSomething( );
}
This test will call CallsDoesSomething, which is the function
we are testing. We are expecting that function to call DoesSomething
three times. The first time, we check to make sure it's called
as DoesSomething(1, 2) and we'll magically return a 3. The second
time we check for DoesSomething(4, 5) and we'll return a 6. The
third time we verify DoesSomething(7, 8) and we'll throw an error
instead of returning anything. If CallsDoesSomething gets
any of this wrong, it fails the test. It will fail if you didn't
call DoesSomething enough, or too much, or with the wrong arguments,
or in the wrong order.
CMock is based on Unity, which it uses for all internal testing.
It uses Ruby to do all the main work (versions 2.0.0 and above).
Installing
==========
The first thing you need to do to install CMock is to get yourself
a copy of Ruby. If you're on linux or osx, you probably already
have it. You can prove it by typing the following:
ruby --version
If it replied in a way that implies ignorance, then you're going to
need to install it. You can go to [ruby-lang](https://ruby-lang.org)
to get the latest version. You're also going to need to do that if it
replied with a version that is older than 2.0.0. Go ahead. We'll wait.
Once you have Ruby, you have three options:
* Clone the latest [CMock repo on github](https://github.com/ThrowTheSwitch/CMock/)
* Download the latest [CMock zip from github](https://github.com/ThrowTheSwitch/CMock/)
* Install Ceedling (which has it built in!) through your commandline using `gem install ceedling`.
Generated Mock Module Summary
=============================
In addition to the mocks themselves, CMock will generate the
following functions for use in your tests. The expect functions
are always generated. The other functions are only generated
if those plugins are enabled:
Expect:
-------
Your basic staple Expects which will be used for most of your day
to day CMock work. By calling this, you are telling CMock that you
expect that function to be called during your test. It also specifies
which arguments you expect it to be called with, and what return
value you want returned when that happens. You can call this function
multiple times back to back in order to queue up multiple calls.
* `void func(void)` => `void func_Expect(void)`
* `void func(params)` => `void func_Expect(expected_params)`
* `retval func(void)` => `void func_ExpectAndReturn(retval_to_return)`
* `retval func(params)` => `void func_ExpectAndReturn(expected_params, retval_to_return)`
ExpectAnyArgs:
--------------
This behaves just like the Expects calls, except that it doesn't really
care what the arguments are that the mock gets called with. It still counts
the number of times the mock is called and it still handles return values
if there are some.
* `void func(void)` => `void func_ExpectAnyArgs(void)`
* `void func(params)` => `void func_ExpectAnyArgs(void)`
* `retval func(void)` => `void func_ExpectAnyArgsAndReturn(retval_to_return)`
* `retval func(params)` => `void func_ExpectAnyArgsAndReturn(retval_to_return)`
Array:
------
An ExpectWithArray is another variant of Expect. Like expect, it cares about
the number of times a mock is called, the arguments it is called with, and the
values it is to return. This variant has another feature, though. For anything
that resembles a pointer or array, it breaks the argument into TWO arguments.
The first is the original pointer. The second specify the number of elements
it is to verify of that array. If you specify 1, it'll check one object. If 2,
it'll assume your pointer is pointing at the first of two elements in an array.
If you specify zero elements, it will check just the pointer if
`:smart` mode is configured or fail if `:compare_data` is set.
* `void func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers)
* `void func(ptr * param, other)` => `void func_ExpectWithArray(ptr* param, int param_depth, other)`
* `retval func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers)
* `retval func(other, ptr* param)` => `void func_ExpectWithArrayAndReturn(other, ptr* param, int param_depth, retval_to_return)`
Ignore:
-------
Maybe you don't care about the number of times a particular function is called or
the actual arguments it is called with. In that case, you want to use Ignore. Ignore
only needs to be called once per test. It will then ignore any further calls to that
particular mock. The IgnoreAndReturn works similarly, except that it has the added
benefit of knowing what to return when that call happens. If the mock is called more
times than IgnoreAndReturn was called, it will keep returning the last value without
complaint. If it's called less times, it will also ignore that. You SAID you didn't
care how many times it was called, right?
* `void func(void)` => `void func_Ignore(void)`
* `void func(params)` => `void func_Ignore(void)`
* `retval func(void)` => `void func_IgnoreAndReturn(retval_to_return)`
* `retval func(params)` => `void func_IgnoreAndReturn(retval_to_return)`
Ignore Arg:
------------
Maybe you overall want to use Expect and its similar variations, but you don't care
what is passed to a particular argument. This is particularly useful when that argument
is a pointer to a value that is supposed to be filled in by the function. You don't want
to use ExpectAnyArgs, because you still care about the other arguments. Instead, before
any of your Expect calls are made, you can call this function. It tells CMock to ignore
a particular argument for the rest of this test, for this mock function.
* `void func(params)` => `void func_IgnoreArg_paramName(void)`
ReturnThruPtr:
--------------
Another option which operates on a particular argument of a function is the ReturnThruPtr
plugin. For every argument that resembles a pointer or reference, CMock generates an
instance of this function. Just as the AndReturn functions support injecting one or more
return values into a queue, this function lets you specify one or more return values which
are queued up and copied into the space being pointed at each time the mock is called.
* `void func(param1)` => `void func_ReturnThruPtr_paramName(val_to_return)`
* => `void func_ReturnArrayThruPtr_paramName(cal_to_return, len)`
* => `void func_ReturnMemThruPtr_paramName(val_to_return, size)`
Callback:
---------
If all those other options don't work, and you really need to do something custom, you
still have a choice. As soon as you stub a callback in a test, it will call the callback
whenever the mock is encountered and return the retval returned from the callback (if any)
instead of performing the usual expect checks. It can be configured to check the arguments
first (like expects) or just jump directly to the callback.
* `void func(void)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
where `CMOCK_func_CALLBACK` looks like: `void func(int NumCalls)`
* `void func(params)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
where `CMOCK_func_CALLBACK` looks like: `void func(params, int NumCalls)`
* `retval func(void)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
where `CMOCK_func_CALLBACK` looks like: `retval func(int NumCalls)`
* `retval func(params)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
where `CMOCK_func_CALLBACK` looks like: `retval func(params, int NumCalls)`
Cexception:
-----------
Finally, if you are using Cexception for error handling, you can use this to throw errors
from inside mocks. Like Expects, it remembers which call was supposed to throw the error,
and it still checks parameters first.
* `void func(void)` => `void func_ExpectAndThrow(value_to_throw)`
* `void func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)`
* `retval func(void)` => `void func_ExpectAndThrow(value_to_throw)`
* `retval func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)`
Running CMock
=============
CMock is a Ruby script and class. You can therefore use it directly
from the command line, or include it in your own scripts or rakefiles.
Mocking from the Command Line
-----------------------------
After unpacking CMock, you will find cmock.rb in the 'lib' directory.
This is the file that you want to run. It takes a list of header files
to be mocked, as well as an optional yaml file for a more detailed
configuration (see config options below).
For example, this will create three mocks using the configuration
specified in MyConfig.yml:
ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h
And this will create two mocks using the default configuration:
ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h
Mocking From Scripts or Rake
----------------------------
CMock can be used directly from your own scripts or from a rakefile.
Start by including cmock.rb, then create an instance of CMock.
When you create your instance, you may initialize it in one of
three ways.
You may specify nothing, allowing it to run with default settings:
require 'cmock.rb'
cmock = CMock.new
You may specify a YAML file containing the configuration options
you desire:
cmock = CMock.new('../MyConfig.yml')
You may specify the options explicitly:
cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => 'my/mocks/')
Config Options:
---------------
The following configuration options can be specified in the
yaml file or directly when instantiating.
Passed as Ruby, they look like this:
{ :attributes => [“__funky”, “__intrinsic”], :when_ptr => :compare }
Defined in the yaml file, they look more like this:
:cmock:
:attributes:
- __funky
- __intrinsic
:when_ptr: :compare
In all cases, you can just include the things that you want to override
from the defaults. We've tried to specify what the defaults are below.
* `:attributes`:
These are attributes that CMock should ignore for you for testing
purposes. Custom compiler extensions and externs are handy things to
put here. If your compiler is choking on some extended syntax, this
is often a good place to look.
* defaults: ['__ramfunc', '__irq', '__fiq', 'register', 'extern']
* **note:** this option will reinsert these attributes onto the mock's calls.
If that isn't what you are looking for, check out :strippables.
* `:c_calling_conventions`:
Similarly, CMock may need to understand which C calling conventions
might show up in your codebase. If it encounters something it doesn't
recognize, it's not going to mock it. We have the most common covered,
but there are many compilers out there, and therefore many other options.
* defaults: ['__stdcall', '__cdecl', '__fastcall']
* **note:** this option will reinsert these attributes onto the mock's calls.
If that isn't what you are looking for, check out :strippables.
* `:callback_after_arg_check`:
Tell `:callback` plugin to do the normal argument checking **before** it
calls the callback function by setting this to true. When false, the
callback function is called **instead** of the argument verification.
* default: false
* `:callback_include_count`:
Tell `:callback` plugin to include an extra parameter to specify the
number of times the callback has been called. If set to false, the
callback has the same interface as the mocked function. This can be
handy when you're wanting to use callback as a stub.
* default: true
* `:cexception_include`:
Tell `:cexception` plugin where to find CException.h... You only need to
define this if it's not in your build path already... which it usually
will be for the purpose of your builds.
* default: *nil*
* `:enforce_strict_ordering`:
CMock always enforces the order that you call a particular function,
so if you expect GrabNabber(int size) to be called three times, it
will verify that the sizes are in the order you specified. You might
*also* want to make sure that all different functions are called in a
particular order. If so, set this to true.
* default: false
* `:framework`:
Currently the only option is `:unity.` Eventually if we support other
unity test frameworks (or if you write one for us), they'll get added
here.
: default: :unity
* `:includes`:
An array of additional include files which should be added to the
mocks. Useful for global types and definitions used in your project.
There are more specific versions if you care WHERE in the mock files
the includes get placed. You can define any or all of these options.
* `:includes`
* `:includes_h_pre_orig_header`
* `:includes_h_post_orig_header`
* `:includes_c_pre_header`
* `:includes_c_post_header`
* default: nil #for all 5 options
* `:memcmp_if_unknown`:
C developers create a lot of types, either through typedef or preprocessor
macros. CMock isn't going to automatically know what you were thinking all
the time (though it tries its best). If it comes across a type it doesn't
recognize, you have a choice on how you want it to handle it. It can either
perform a raw memory comparison and report any differences, or it can fail
with a meaningful message. Either way, this feature will only happen after
all other mechanisms have failed (The thing encountered isn't a standard
type. It isn't in the :treat_as list. It isn't in a custom unity_helper).
* default: true
* `:mock_path`:
The directory where you would like the mock files generated to be
placed.
* default: mocks
* `:mock_prefix`:
The prefix to prepend to your mock files. For example, if it's “Mock”, a file
“USART.h” will get a mock called “MockUSART.c”. This CAN be used with a suffix
at the same time.
* default: Mock
* `:mock_suffix`:
The suffix to append to your mock files. For example, it it's "_Mock", a file
"USART.h" will get a mock called "USART_Mock.h". This CAN be used with a prefix
at the same time.
* default: ""
* `:plugins`:
An array of which plugins to enable. ':expect' is always active. Also
available currently:
* `:ignore`
* `:ignore_arg`
* `:expect_any_args`
* `:array`
* `:cexception`
* `:callback`
* `:return_thru_ptr`
* `:strippables`:
An array containing a list of items to remove from the header
before deciding what should be mocked. This can be something simple
like a compiler extension CMock wouldn't recognize, or could be a
regex to reject certain function name patterns. This is a great way to
get rid of compiler extensions when your test compiler doesn't support
them. For example, use `:strippables: ['(?:functionName\s*\(+.*?\)+)']`
to prevent a function `functionName` from being mocked. By default, it
is ignoring all gcc attribute extensions.
* default: ['(?:__attribute__\s*\(+.*?\)+)']
* `:subdir`:
This is a relative subdirectory for your mocks. Set this to e.g. "sys" in
order to create a mock for `sys/types.h` in `(:mock_path)/sys/`.
* default: ""
* `:treat_as`:
The `:treat_as` list is a shortcut for when you have created typedefs
of standard types. Why create a custom unity helper for UINT16 when
the unity function TEST_ASSERT_EQUAL_HEX16 will work just perfectly?
Just add 'UINT16' => 'HEX16' to your list (actually, don't. We already
did that one for you). Maybe you have a type that is a pointer to an
array of unsigned characters? No problem, just add 'UINT8_T*' =>
'HEX8*'
* NOTE: unlike the other options, your specifications MERGE with the
default list. Therefore, if you want to override something, you must
reassign it to something else (or to *nil* if you don't want it)
* default:
* 'int': 'INT'
* 'char': 'INT8'
* 'short': 'INT16'
* 'long': 'INT'
* 'int8': 'INT8'
* 'int16': 'INT16'
* 'int32': 'INT'
* 'int8_t': 'INT8'
* 'int16_t': 'INT16'
* 'int32_t': 'INT'
* 'INT8_T': 'INT8'
* 'INT16_T': 'INT16'
* 'INT32_T': 'INT'
* 'bool': 'INT'
* 'bool_t': 'INT'
* 'BOOL': 'INT'
* 'BOOL_T': 'INT'
* 'unsigned int': 'HEX32'
* 'unsigned long': 'HEX32'
* 'uint32': 'HEX32'
* 'uint32_t': 'HEX32'
* 'UINT32': 'HEX32'
* 'UINT32_T': 'HEX32'
* 'void*': 'HEX8_ARRAY'
* 'unsigned short': 'HEX16'
* 'uint16': 'HEX16'
* 'uint16_t': 'HEX16'
* 'UINT16': 'HEX16'
* 'UINT16_T': 'HEX16'
* 'unsigned char': 'HEX8'
* 'uint8': 'HEX8'
* 'uint8_t': 'HEX8'
* 'UINT8': 'HEX8'
* 'UINT8_T': 'HEX8'
* 'char*': 'STRING'
* 'pCHAR': 'STRING'
* 'cstring': 'STRING'
* 'CSTRING': 'STRING'
* 'float': 'FLOAT'
* 'double': 'FLOAT'
* `:treat_as_void`:
We've seen "fun" legacy systems typedef 'void' with a custom type,
like MY_VOID. Add any instances of those to this list to help CMock
understand how to deal with your code.
* default: []
* `:treat_externs`:
This specifies how you want CMock to handle functions that have been
marked as extern in the header file. Should it mock them?
* `:include` will mock externed functions
* `:exclude` will ignore externed functions (default).
* `:unity_helper_path`:
If you have created a header with your own extensions to unity to
handle your own types, you can set this argument to that path. CMock
will then automagically pull in your helpers and use them. The only
trick is that you make sure you follow the naming convention:
`UNITY_TEST_ASSERT_EQUAL_YourType`. If it finds macros of the right
shape that match that pattern, it'll use them.
* default: []
* `:verbosity`:
How loud should CMock be?
* 0 for errors only
* 1 for errors and warnings
* 2 for normal (default)
* 3 for verbose
* `: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
in particular tests.
* Set to '__attribute ((weak))' for weak mocks when using GCC.
* Set to any non-empty string for weak mocks when using IAR.
* default: ""
* `:when_no_prototypes`:
When you give CMock a header file and ask it to create a mock out of
it, it usually contains function prototypes (otherwise what was the
point?). You can control what happens when this isn't true. You can
set this to `:warn,` `:ignore,` or `:error`
* default: :warn
* `:when_ptr`:
You can customize how CMock deals with pointers (c strings result in
string comparisons... we're talking about **other** pointers here). Your
options are `:compare_ptr` to just verify the pointers are the same,
`:compare_data` or `:smart` to verify that the data is the same.
`:compare_data` and `:smart` behaviors will change slightly based on
if you have the array plugin enabled. By default, they compare a
single element of what is being pointed to. So if you have a pointer
to a struct called ORGAN_T, it will compare one ORGAN_T (whatever that
is).
* default: :smart
* `:fail_on_unexpected_calls`:
By default, CMock will fail a test if a mock is called without _Expect and _Ignore
called first. While this forces test writers to be more explicit in their expectations,
it can clutter tests with _Expect or _Ignore calls for functions which are not the focus
of the test. While this is a good indicator that this module should be refactored, some
users are not fans of the additional noise.
Therefore, :fail_on_unexpected_calls can be set to false to force all mocks to start with
the assumption that they are operating as _Ignore unless otherwise specified.
* default: true
* **note:**
If this option is disabled, the mocked functions will return
a default value (0) when called (and only if they have to return something of course).
Compiled Options:
-----------------
A number of #defines also exist for customizing the cmock experience.
Feel free to pass these into your compiler or whatever is most
convenient. CMock will otherwise do its best to guess what you want
based on other settings, particularly Unity's settings.
* `CMOCK_MEM_STATIC` or `CMOCK_MEM_DYNAMIC`
Define one of these to determine if you want to dynamically add
memory during tests as required from the heap. If static, you
can control the total footprint of Cmock. If dynamic, you will
need to make sure you make some heap space available for Cmock.
* `CMOCK_MEM_SIZE`
In static mode this is the total amount of memory you are allocating
to Cmock. In Dynamic mode this is the size of each chunk allocated
at once (larger numbers grab more memory but require less mallocs).
* `CMOCK_MEM_ALIGN`
The way to align your data to. Not everything is as flexible as
a PC, as most embedded designers know. This defaults to 2, meaning
align to the closest 2^2 -> 4 bytes (32 bits). You can turn off alignment
by setting 0, force alignment to the closest uint16 with 1 or even
to the closest uint64 with 3.
* `CMOCK_MEM_PTR_AS_INT`
This is used internally to hold pointers... it needs to be big
enough. On most processors a pointer is the same as an unsigned
long... but maybe that's not true for yours?
* `CMOCK_MEM_INDEX_TYPE`
This needs to be something big enough to point anywhere in Cmock's
memory space... usually it's an unsigned int.
Examples
========
You can look in the [examples directory](/examples/) for a couple of examples on how
you might tool CMock into your build process. You may also want to consider
using [Ceedling](https://throwtheswitch.org/ceedling). Please note that
these examples are meant to show how the build process works. They have
failing tests ON PURPOSE to show what that would look like. Don't be alarmed. ;)

File diff suppressed because it is too large Load Diff

View File

@ -1,206 +0,0 @@
# ThrowTheSwitch.org Coding Standard
Hi. Welcome to the coding standard for ThrowTheSwitch.org. For the most part,
we try to follow these standards to unify our contributors' code into a cohesive
unit (puns intended). You might find places where these standards aren't
followed. We're not perfect. Please be polite where you notice these discrepancies
and we'll try to be polite when we notice yours.
;)
## Why Have A Coding Standard?
Being consistent makes code easier to understand. We've tried to keep
our standard simple because we also believe that we can only expect someone to
follow something that is understandable. Please do your best.
## Our Philosophy
Before we get into details on syntax, let's take a moment to talk about our
vision for these tools. We're C developers and embedded software developers.
These tools are great to test any C code, but catering to embedded software has
made us more tolerant of compiler quirks. There are a LOT of quirky compilers
out there. By quirky I mean "doesn't follow standards because they feel like
they have a license to do as they wish."
Our philosophy is "support every compiler we can". Most often, this means that
we aim for writing C code that is standards compliant (often C89... that seems
to be a sweet spot that is almost always compatible). But it also means these
tools are tolerant of things that aren't common. Some that aren't even
compliant. There are configuration options to override the size of standard
types. There are configuration options to force Unity to not use certain
standard library functions. A lot of Unity is configurable and we have worked
hard to make it not TOO ugly in the process.
Similarly, our tools that parse C do their best. They aren't full C parsers
(yet) and, even if they were, they would still have to accept non-standard
additions like gcc extensions or specifying `@0x1000` to force a variable to
compile to a particular location. It's just what we do, because we like
everything to Just Work™.
Speaking of having things Just Work™, that's our second philosophy. By that, we
mean that we do our best to have EVERY configuration option have a logical
default. We believe that if you're working with a simple compiler and target,
you shouldn't need to configure very much... we try to make the tools guess as
much as they can, but give the user the power to override it when it's wrong.
## Naming Things
Let's talk about naming things. Programming is all about naming things. We name
files, functions, variables, and so much more. While we're not always going to
find the best name for something, we actually put a bit of effort into
finding *What Something WANTS to be Called*™.
When naming things, we follow this hierarchy, the first being the
most important to us (but we do all four when possible):
1. Readable
2. Descriptive
3. Consistent
4. Memorable
#### Readable
We want to read our code. This means we like names and flow that are more
naturally read. We try to avoid double negatives. We try to avoid cryptic
abbreviations (sticking to ones we feel are common).
#### Descriptive
We like descriptive names for things, especially functions and variables.
Finding the right name for something is an important endeavor. You might notice
from poking around our code that this often results in names that are a little
longer than the average. Guilty. We're okay with a bit more typing if it
means our code is easier to understand.
There are two exceptions to this rule that we also stick to as religiously as
possible:
First, while we realize hungarian notation (and similar systems for encoding
type information into variable names) is providing a more descriptive name, we
feel that (for the average developer) it takes away from readability and is to be avoided.
Second, loop counters and other local throw-away variables often have a purpose
which is obvious. There's no need, therefore, to get carried away with complex
naming. We find i, j, and k are better loop counters than loopCounterVar or
whatnot. We only break this rule when we see that more description could improve
understanding of an algorithm.
#### Consistent
We like consistency, but we're not really obsessed with it. We try to name our
configuration macros in a consistent fashion... you'll notice a repeated use of
UNITY_EXCLUDE_BLAH or UNITY_USES_BLAH macros. This helps users avoid having to
remember each macro's details.
#### Memorable
Where ever it doesn't violate the above principles, we try to apply memorable
names. Sometimes this means using something that is simply descriptive, but
often we strive for descriptive AND unique... we like quirky names that stand
out in our memory and are easier to search for. Take a look through the file
names in Ceedling and you'll get a good idea of what we are talking about here.
Why use preprocess when you can use preprocessinator? Or what better describes a
module in charge of invoking tasks during releases than release_invoker? Don't
get carried away. The names are still descriptive and fulfill the above
requirements, but they don't feel stale.
## C and C++ Details
We don't really want to add to the style battles out there. Tabs or spaces?
How many spaces? Where do the braces go? These are age-old questions that will
never be answered... or at least not answered in a way that will make everyone
happy.
We've decided on our own style preferences. If you'd like to contribute to these
projects (and we hope that you do), then we ask if you do your best to follow
the same. It will only hurt a little. We promise.
#### Whitespace
Our C-style is to use spaces and to use 4 of them per indent level. It's a nice
power-of-2 number that looks decent on a wide-screen. We have no more reason
than that. We break that rule when we have lines that wrap (macros or function
arguments or whatnot). When that happens, we like to indent further to line
things up in nice tidy columns.
```C
if (stuff_happened)
{
do_something();
}
```
#### Case
- Files - all lower case with underscores.
- Variables - all lower case with underscores
- Macros - all caps with underscores.
- Typedefs - all caps with underscores. (also ends with _T).
- Functions - camel cased. Usually named ModuleName_FuncName
- Constants and Globals - camel cased.
#### Braces
The left brace is on the next line after the declaration. The right brace is
directly below that. Everything in between in indented one level. If you're
catching an error and you have a one-line, go ahead and to it on the same line.
```C
while (blah)
{
//Like so. Even if only one line, we use braces.
}
```
#### Comments
Do you know what we hate? Old-school C block comments. BUT, we're using them
anyway. As we mentioned, our goal is to support every compiler we can,
especially embedded compilers. There are STILL C compilers out there that only
support old-school block comments. So that is what we're using. We apologize. We
think they are ugly too.
## Ruby Details
Is there really such thing as a Ruby coding standard? Ruby is such a free form
language, it seems almost sacrilegious to suggest that people should comply to
one method! We'll keep it really brief!
#### Whitespace
Our Ruby style is to use spaces and to use 2 of them per indent level. It's a
nice power-of-2 number that really grooves with Ruby's compact style. We have no
more reason than that. We break that rule when we have lines that wrap. When
that happens, we like to indent further to line things up in nice tidy columns.
#### Case
- Files - all lower case with underscores.
- Variables - all lower case with underscores
- Classes, Modules, etc - Camel cased.
- Functions - all lower case with underscores
- Constants - all upper case with underscores
## Documentation
Egad. Really? We use mark down and we like pdf files because they can be made to
look nice while still being portable. Good enough?
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*

View File

@ -1,779 +0,0 @@
# Unity Assertions Reference
## Background and Overview
### Super Condensed Version
- An assertion establishes truth (i.e. boolean True) for a single condition.
Upon boolean False, an assertion stops execution and reports the failure.
- Unity is mainly a rich collection of assertions and the support to gather up
and easily execute those assertions.
- The structure of Unity allows you to easily separate test assertions from
source code in, well, test code.
- Unity's assertions:
- Come in many, many flavors to handle different C types and assertion cases.
- Use context to provide detailed and helpful failure messages.
- Document types, expected values, and basic behavior in your source code for
free.
### Unity Is Several Things But Mainly It's Assertions
One way to think of Unity is simply as a rich collection of assertions you can
use to establish whether your source code behaves the way you think it does.
Unity provides a framework to easily organize and execute those assertions in
test code separate from your source code.
### What's an Assertion?
At their core, assertions are an establishment of truth - boolean truth. Was this
thing equal to that thing? Does that code doohickey have such-and-such property
or not? You get the idea. Assertions are executable code (to appreciate the big
picture on this read up on the difference between
[link:Dynamic Verification and Static Analysis]). A failing assertion stops
execution and reports an error through some appropriate I/O channel (e.g.
stdout, GUI, file, blinky light).
Fundamentally, for dynamic verification all you need is a single assertion
mechanism. In fact, that's what the [assert() macro in C's standard library](http://en.wikipedia.org/en/wiki/Assert.h)
is for. So why not just use it? Well, we can do far better in the reporting
department. C's `assert()` is pretty dumb as-is and is particularly poor for
handling common data types like arrays, structs, etc. And, without some other
support, it's far too tempting to litter source code with C's `assert()`'s. It's
generally much cleaner, manageable, and more useful to separate test and source
code in the way Unity facilitates.
### Unity's Assertions: Helpful Messages _and_ Free Source Code Documentation
Asserting a simple truth condition is valuable, but using the context of the
assertion is even more valuable. For instance, if you know you're comparing bit
flags and not just integers, then why not use that context to give explicit,
readable, bit-level feedback when an assertion fails?
That's what Unity's collection of assertions do - capture context to give you
helpful, meaningful assertion failure messages. In fact, the assertions
themselves also serve as executable documentation about types and values in your
source code. So long as your tests remain current with your source and all those
tests pass, you have a detailed, up-to-date view of the intent and mechanisms in
your source code. And due to a wondrous mystery, well-tested code usually tends
to be well designed code.
## Assertion Conventions and Configurations
### Naming and Parameter Conventions
The convention of assertion parameters generally follows this order:
TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} )
The very simplest assertion possible uses only a single "actual" parameter (e.g.
a simple null check).
"Actual" is the value being tested and unlike the other parameters in an
assertion construction is the only parameter present in all assertion variants.
"Modifiers" are masks, ranges, bit flag specifiers, floating point deltas.
"Expected" is your expected value (duh) to compare to an "actual" value; it's
marked as an optional parameter because some assertions only need a single
"actual" parameter (e.g. null check).
"Size/count" refers to string lengths, number of array elements, etc.
Many of Unity's assertions are clear duplications in that the same data type
is handled by several assertions. The differences among these are in how failure
messages are presented. For instance, a `_HEX` variant of an assertion prints
the expected and actual values of that assertion formatted as hexadecimal.
#### TEST_ASSERT_X_MESSAGE Variants
_All_ assertions are complemented with a variant that includes a simple string
message as a final parameter. The string you specify is appended to an assertion
failure message in Unity output.
For brevity, the assertion variants with a message parameter are not listed
below. Just tack on `_MESSAGE` as the final component to any assertion name in
the reference list below and add a string as the final parameter.
_Example:_
TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} )
becomes messageified like thus...
TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message )
Notes:
- The `_MESSAGE` variants intentionally do not support `printf` style formatting
since many embedded projects don't support or avoid `printf` for various reasons.
It is possible to use `sprintf` before the assertion to assemble a complex fail
message, if necessary.
- If you want to output a counter value within an assertion fail message (e.g. from
a loop) , building up an array of results and then using one of the `_ARRAY`
assertions (see below) might be a handy alternative to `sprintf`.
#### TEST_ASSERT_X_ARRAY Variants
Unity provides a collection of assertions for arrays containing a variety of
types. These are documented in the Array section below. These are almost on par
with the `_MESSAGE`variants of Unity's Asserts in that for pretty much any Unity
type assertion you can tack on `_ARRAY` and run assertions on an entire block of
memory.
TEST_ASSERT_EQUAL_TYPEX_ARRAY( expected, actual, {size/count} )
"Expected" is an array itself.
"Size/count" is one or two parameters necessary to establish the number of array
elements and perhaps the length of elements within the array.
Notes:
- The `_MESSAGE` variant convention still applies here to array assertions. The
`_MESSAGE` variants of the `_ARRAY` assertions have names ending with
`_ARRAY_MESSAGE`.
- Assertions for handling arrays of floating point values are grouped with float
and double assertions (see immediately following section).
### TEST_ASSERT_EACH_EQUAL_X Variants
Unity provides a collection of assertions for arrays containing a variety of
types which can be compared to a single value as well. These are documented in
the Each Equal section below. these are almost on par with the `_MESSAGE`
variants of Unity's Asserts in that for pretty much any Unity type assertion you
can inject _EACH_EQUAL and run assertions on an entire block of memory.
TEST_ASSERT_EACH_EQUAL_TYPEX( expected, actual, {size/count} )
"Expected" is a single value to compare to.
"Actual" is an array where each element will be compared to the expected value.
"Size/count" is one of two parameters necessary to establish the number of array
elements and perhaps the length of elements within the array.
Notes:
- The `_MESSAGE` variant convention still applies here to Each Equal assertions.
- Assertions for handling Each Equal of floating point values are grouped with
float and double assertions (see immediately following section).
### Configuration
#### Floating Point Support Is Optional
Support for floating point types is configurable. That is, by defining the
appropriate preprocessor symbols, floats and doubles can be individually enabled
or disabled in Unity code. This is useful for embedded targets with no floating
point math support (i.e. Unity compiles free of errors for fixed point only
platforms). See Unity documentation for specifics.
#### Maximum Data Type Width Is Configurable
Not all targets support 64 bit wide types or even 32 bit wide types. Define the
appropriate preprocessor symbols and Unity will omit all operations from
compilation that exceed the maximum width of your target. See Unity
documentation for specifics.
## The Assertions in All Their Blessed Glory
### Basic Fail and Ignore
##### `TEST_FAIL()`
This fella is most often used in special conditions where your test code is
performing logic beyond a simple assertion. That is, in practice, `TEST_FAIL()`
will always be found inside a conditional code block.
_Examples:_
- Executing a state machine multiple times that increments a counter your test
code then verifies as a final step.
- Triggering an exception and verifying it (as in Try / Catch / Throw - see the
[CException](https://github.com/ThrowTheSwitch/CException) project).
##### `TEST_IGNORE()`
Marks a test case (i.e. function meant to contain test assertions) as ignored.
Usually this is employed as a breadcrumb to come back and implement a test case.
An ignored test case has effects if other assertions are in the enclosing test
case (see Unity documentation for more).
### Boolean
##### `TEST_ASSERT (condition)`
##### `TEST_ASSERT_TRUE (condition)`
##### `TEST_ASSERT_FALSE (condition)`
##### `TEST_ASSERT_UNLESS (condition)`
A simple wording variation on `TEST_ASSERT_FALSE`.The semantics of
`TEST_ASSERT_UNLESS` aid readability in certain test constructions or
conditional statements.
##### `TEST_ASSERT_NULL (pointer)`
##### `TEST_ASSERT_NOT_NULL (pointer)`
### Signed and Unsigned Integers (of all sizes)
Large integer sizes can be disabled for build targets that do not support them.
For example, if your target only supports up to 16 bit types, by defining the
appropriate symbols Unity can be configured to omit 32 and 64 bit operations
that would break compilation (see Unity documentation for more). Refer to
Advanced Asserting later in this document for advice on dealing with other word
sizes.
##### `TEST_ASSERT_EQUAL_INT (expected, actual)`
##### `TEST_ASSERT_EQUAL_INT8 (expected, actual)`
##### `TEST_ASSERT_EQUAL_INT16 (expected, actual)`
##### `TEST_ASSERT_EQUAL_INT32 (expected, actual)`
##### `TEST_ASSERT_EQUAL_INT64 (expected, actual)`
##### `TEST_ASSERT_EQUAL (expected, actual)`
##### `TEST_ASSERT_NOT_EQUAL (expected, actual)`
##### `TEST_ASSERT_EQUAL_UINT (expected, actual)`
##### `TEST_ASSERT_EQUAL_UINT8 (expected, actual)`
##### `TEST_ASSERT_EQUAL_UINT16 (expected, actual)`
##### `TEST_ASSERT_EQUAL_UINT32 (expected, actual)`
##### `TEST_ASSERT_EQUAL_UINT64 (expected, actual)`
### Unsigned Integers (of all sizes) in Hexadecimal
All `_HEX` assertions are identical in function to unsigned integer assertions
but produce failure messages with the `expected` and `actual` values formatted
in hexadecimal. Unity output is big endian.
##### `TEST_ASSERT_EQUAL_HEX (expected, actual)`
##### `TEST_ASSERT_EQUAL_HEX8 (expected, actual)`
##### `TEST_ASSERT_EQUAL_HEX16 (expected, actual)`
##### `TEST_ASSERT_EQUAL_HEX32 (expected, actual)`
##### `TEST_ASSERT_EQUAL_HEX64 (expected, actual)`
### Masked and Bit-level Assertions
Masked and bit-level assertions produce output formatted in hexadecimal. Unity
output is big endian.
##### `TEST_ASSERT_BITS (mask, expected, actual)`
Only compares the masked (i.e. high) bits of `expected` and `actual` parameters.
##### `TEST_ASSERT_BITS_HIGH (mask, actual)`
Asserts the masked bits of the `actual` parameter are high.
##### `TEST_ASSERT_BITS_LOW (mask, actual)`
Asserts the masked bits of the `actual` parameter are low.
##### `TEST_ASSERT_BIT_HIGH (bit, actual)`
Asserts the specified bit of the `actual` parameter is high.
##### `TEST_ASSERT_BIT_LOW (bit, actual)`
Asserts the specified bit of the `actual` parameter is low.
### Integer Less Than / Greater Than
These assertions verify that the `actual` parameter is less than or greater
than `threshold` (exclusive). For example, if the threshold value is 0 for the
greater than assertion will fail if it is 0 or less.
##### `TEST_ASSERT_GREATER_THAN (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_INT (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_INT8 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_INT16 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_INT32 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_UINT (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_UINT8 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_UINT16 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_UINT32 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_HEX8 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_HEX16 (threshold, actual)`
##### `TEST_ASSERT_GREATER_THAN_HEX32 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_INT (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_INT8 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_INT16 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_INT32 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_UINT (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_UINT8 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_UINT16 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_UINT32 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_HEX8 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_HEX16 (threshold, actual)`
##### `TEST_ASSERT_LESS_THAN_HEX32 (threshold, actual)`
### Integer Ranges (of all sizes)
These assertions verify that the `expected` parameter is within +/- `delta`
(inclusive) of the `actual` parameter. For example, if the expected value is 10
and the delta is 3 then the assertion will fail for any value outside the range
of 7 - 13.
##### `TEST_ASSERT_INT_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_INT8_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_INT16_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_INT32_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_INT64_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_UINT_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_UINT8_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_UINT16_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_UINT32_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_UINT64_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_HEX_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_HEX8_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_HEX16_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_HEX32_WITHIN (delta, expected, actual)`
##### `TEST_ASSERT_HEX64_WITHIN (delta, expected, actual)`
### Structs and Strings
##### `TEST_ASSERT_EQUAL_PTR (expected, actual)`
Asserts that the pointers point to the same memory location.
##### `TEST_ASSERT_EQUAL_STRING (expected, actual)`
Asserts that the null terminated (`'\0'`)strings are identical. If strings are
of different lengths or any portion of the strings before their terminators
differ, the assertion fails. Two NULL strings (i.e. zero length) are considered
equivalent.
##### `TEST_ASSERT_EQUAL_MEMORY (expected, actual, len)`
Asserts that the contents of the memory specified by the `expected` and `actual`
pointers is identical. The size of the memory blocks in bytes is specified by
the `len` parameter.
### Arrays
`expected` and `actual` parameters are both arrays. `num_elements` specifies the
number of elements in the arrays to compare.
`_HEX` assertions produce failure messages with expected and actual array
contents formatted in hexadecimal.
For array of strings comparison behavior, see comments for
`TEST_ASSERT_EQUAL_STRING` in the preceding section.
Assertions fail upon the first element in the compared arrays found not to
match. Failure messages specify the array index of the failed comparison.
##### `TEST_ASSERT_EQUAL_INT_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_INT8_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_INT16_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_INT32_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_INT64_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_UINT_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_UINT8_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_UINT16_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_UINT32_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_UINT64_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_HEX_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_HEX8_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_HEX16_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_HEX32_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_HEX64_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_PTR_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_STRING_ARRAY (expected, actual, num_elements)`
##### `TEST_ASSERT_EQUAL_MEMORY_ARRAY (expected, actual, len, num_elements)`
`len` is the memory in bytes to be compared at each array element.
### Each Equal (Arrays to Single Value)
`expected` are single values and `actual` are arrays. `num_elements` specifies
the number of elements in the arrays to compare.
`_HEX` assertions produce failure messages with expected and actual array
contents formatted in hexadecimal.
Assertions fail upon the first element in the compared arrays found not to
match. Failure messages specify the array index of the failed comparison.
#### `TEST_ASSERT_EACH_EQUAL_INT (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_INT8 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_INT16 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_INT32 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_INT64 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_UINT (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_UINT8 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_UINT16 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_UINT32 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_UINT64 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_HEX (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_HEX8 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_HEX16 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_HEX32 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_HEX64 (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_PTR (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_STRING (expected, actual, num_elements)`
#### `TEST_ASSERT_EACH_EQUAL_MEMORY (expected, actual, len, num_elements)`
`len` is the memory in bytes to be compared at each array element.
### Floating Point (If enabled)
##### `TEST_ASSERT_FLOAT_WITHIN (delta, expected, actual)`
Asserts that the `actual` value is within +/- `delta` of the `expected` value.
The nature of floating point representation is such that exact evaluations of
equality are not guaranteed.
##### `TEST_ASSERT_EQUAL_FLOAT (expected, actual)`
Asserts that the ?actual?value is "close enough to be considered equal" to the
`expected` value. If you are curious about the details, refer to the Advanced
Asserting section for more details on this. Omitting a user-specified delta in a
floating point assertion is both a shorthand convenience and a requirement of
code generation conventions for CMock.
##### `TEST_ASSERT_EQUAL_FLOAT_ARRAY (expected, actual, num_elements)`
See Array assertion section for details. Note that individual array element
float comparisons are executed using T?EST_ASSERT_EQUAL_FLOAT?.That is, user
specified delta comparison values requires a custom-implemented floating point
array assertion.
##### `TEST_ASSERT_FLOAT_IS_INF (actual)`
Asserts that `actual` parameter is equivalent to positive infinity floating
point representation.
##### `TEST_ASSERT_FLOAT_IS_NEG_INF (actual)`
Asserts that `actual` parameter is equivalent to negative infinity floating
point representation.
##### `TEST_ASSERT_FLOAT_IS_NAN (actual)`
Asserts that `actual` parameter is a Not A Number floating point representation.
##### `TEST_ASSERT_FLOAT_IS_DETERMINATE (actual)`
Asserts that ?actual?parameter is a floating point representation usable for
mathematical operations. That is, the `actual` parameter is neither positive
infinity nor negative infinity nor Not A Number floating point representations.
##### `TEST_ASSERT_FLOAT_IS_NOT_INF (actual)`
Asserts that `actual` parameter is a value other than positive infinity floating
point representation.
##### `TEST_ASSERT_FLOAT_IS_NOT_NEG_INF (actual)`
Asserts that `actual` parameter is a value other than negative infinity floating
point representation.
##### `TEST_ASSERT_FLOAT_IS_NOT_NAN (actual)`
Asserts that `actual` parameter is a value other than Not A Number floating
point representation.
##### `TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE (actual)`
Asserts that `actual` parameter is not usable for mathematical operations. That
is, the `actual` parameter is either positive infinity or negative infinity or
Not A Number floating point representations.
### Double (If enabled)
##### `TEST_ASSERT_DOUBLE_WITHIN (delta, expected, actual)`
Asserts that the `actual` value is within +/- `delta` of the `expected` value.
The nature of floating point representation is such that exact evaluations of
equality are not guaranteed.
##### `TEST_ASSERT_EQUAL_DOUBLE (expected, actual)`
Asserts that the `actual` value is "close enough to be considered equal" to the
`expected` value. If you are curious about the details, refer to the Advanced
Asserting section for more details. Omitting a user-specified delta in a
floating point assertion is both a shorthand convenience and a requirement of
code generation conventions for CMock.
##### `TEST_ASSERT_EQUAL_DOUBLE_ARRAY (expected, actual, num_elements)`
See Array assertion section for details. Note that individual array element
double comparisons are executed using `TEST_ASSERT_EQUAL_DOUBLE`.That is, user
specified delta comparison values requires a custom implemented double array
assertion.
##### `TEST_ASSERT_DOUBLE_IS_INF (actual)`
Asserts that `actual` parameter is equivalent to positive infinity floating
point representation.
##### `TEST_ASSERT_DOUBLE_IS_NEG_INF (actual)`
Asserts that `actual` parameter is equivalent to negative infinity floating point
representation.
##### `TEST_ASSERT_DOUBLE_IS_NAN (actual)`
Asserts that `actual` parameter is a Not A Number floating point representation.
##### `TEST_ASSERT_DOUBLE_IS_DETERMINATE (actual)`
Asserts that `actual` parameter is a floating point representation usable for
mathematical operations. That is, the ?actual?parameter is neither positive
infinity nor negative infinity nor Not A Number floating point representations.
##### `TEST_ASSERT_DOUBLE_IS_NOT_INF (actual)`
Asserts that `actual` parameter is a value other than positive infinity floating
point representation.
##### `TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF (actual)`
Asserts that `actual` parameter is a value other than negative infinity floating
point representation.
##### `TEST_ASSERT_DOUBLE_IS_NOT_NAN (actual)`
Asserts that `actual` parameter is a value other than Not A Number floating
point representation.
##### `TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE (actual)`
Asserts that `actual` parameter is not usable for mathematical operations. That
is, the `actual` parameter is either positive infinity or negative infinity or
Not A Number floating point representations.
## Advanced Asserting: Details On Tricky Assertions
This section helps you understand how to deal with some of the trickier
assertion situations you may run into. It will give you a glimpse into some of
the under-the-hood details of Unity's assertion mechanisms. If you're one of
those people who likes to know what is going on in the background, read on. If
not, feel free to ignore the rest of this document until you need it.
### How do the EQUAL assertions work for FLOAT and DOUBLE?
As you may know, directly checking for equality between a pair of floats or a
pair of doubles is sloppy at best and an outright no-no at worst. Floating point
values can often be represented in multiple ways, particularly after a series of
operations on a value. Initializing a variable to the value of 2.0 is likely to
result in a floating point representation of 2 x 20,but a series of
mathematical operations might result in a representation of 8 x 2-2
that also evaluates to a value of 2. At some point repeated operations cause
equality checks to fail.
So Unity doesn't do direct floating point comparisons for equality. Instead, it
checks if two floating point values are "really close." If you leave Unity
running with defaults, "really close" means "within a significant bit or two."
Under the hood, `TEST_ASSERT_EQUAL_FLOAT` is really `TEST_ASSERT_FLOAT_WITHIN`
with the `delta` parameter calculated on the fly. For single precision, delta is
the expected value multiplied by 0.00001, producing a very small proportional
range around the expected value.
If you are expecting a value of 20,000.0 the delta is calculated to be 0.2. So
any value between 19,999.8 and 20,000.2 will satisfy the equality check. This
works out to be roughly a single bit of range for a single-precision number, and
that's just about as tight a tolerance as you can reasonably get from a floating
point value.
So what happens when it's zero? Zero - even more than other floating point
values - can be represented many different ways. It doesn't matter if you have
0 x 20 or 0 x 263.It's still zero, right? Luckily, if you
subtract these values from each other, they will always produce a difference of
zero, which will still fall between 0 plus or minus a delta of 0. So it still
works!
Double precision floating point numbers use a much smaller multiplier, again
approximating a single bit of error.
If you don't like these ranges and you want to make your floating point equality
assertions less strict, you can change these multipliers to whatever you like by
defining UNITY_FLOAT_PRECISION and UNITY_DOUBLE_PRECISION. See Unity
documentation for more.
### How do we deal with targets with non-standard int sizes?
It's "fun" that C is a standard where something as fundamental as an integer
varies by target. According to the C standard, an `int` is to be the target's
natural register size, and it should be at least 16-bits and a multiple of a
byte. It also guarantees an order of sizes:
```C
char <= short <= int <= long <= long long
```
Most often, `int` is 32-bits. In many cases in the embedded world, `int` is
16-bits. There are rare microcontrollers out there that have 24-bit integers,
and this remains perfectly standard C.
To make things even more interesting, there are compilers and targets out there
that have a hard choice to make. What if their natural register size is 10-bits
or 12-bits? Clearly they can't fulfill _both_ the requirement to be at least
16-bits AND the requirement to match the natural register size. In these
situations, they often choose the natural register size, leaving us with
something like this:
```C
char (8 bit) <= short (12 bit) <= int (12 bit) <= long (16 bit)
```
Um... yikes. It's obviously breaking a rule or two... but they had to break SOME
rules, so they made a choice.
When the C99 standard rolled around, it introduced alternate standard-size types.
It also introduced macros for pulling in MIN/MAX values for your integer types.
It's glorious! Unfortunately, many embedded compilers can't be relied upon to
use the C99 types (Sometimes because they have weird register sizes as described
above. Sometimes because they don't feel like it?).
A goal of Unity from the beginning was to support every combination of
microcontroller or microprocessor and C compiler. Over time, we've gotten really
close to this. There are a few tricks that you should be aware of, though, if
you're going to do this effectively on some of these more idiosyncratic targets.
First, when setting up Unity for a new target, you're going to want to pay
special attention to the macros for automatically detecting types
(where available) or manually configuring them yourself. You can get information
on both of these in Unity's documentation.
What about the times where you suddenly need to deal with something odd, like a
24-bit `int`? The simplest solution is to use the next size up. If you have a
24-bit `int`, configure Unity to use 32-bit integers. If you have a 12-bit
`int`, configure Unity to use 16 bits. There are two ways this is going to
affect you:
1. When Unity displays errors for you, it's going to pad the upper unused bits
with zeros.
2. You're going to have to be careful of assertions that perform signed
operations, particularly `TEST_ASSERT_INT_WITHIN`.Such assertions might wrap
your `int` in the wrong place, and you could experience false failures. You can
always back down to a simple `TEST_ASSERT` and do the operations yourself.
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*

View File

@ -1,433 +0,0 @@
# Unity Configuration Guide
## C Standards, Compilers and Microcontrollers
The embedded software world contains its challenges. Compilers support different
revisions of the C Standard. They ignore requirements in places, sometimes to
make the language more usable in some special regard. Sometimes it's to simplify
their support. Sometimes it's due to specific quirks of the microcontroller they
are targeting. Simulators add another dimension to this menagerie.
Unity is designed to run on almost anything that is targeted by a C compiler. It
would be awesome if this could be done with zero configuration. While there are
some targets that come close to this dream, it is sadly not universal. It is
likely that you are going to need at least a couple of the configuration options
described in this document.
All of Unity's configuration options are `#defines`. Most of these are simple
definitions. A couple are macros with arguments. They live inside the
unity_internals.h header file. We don't necessarily recommend opening that file
unless you really need to. That file is proof that a cross-platform library is
challenging to build. From a more positive perspective, it is also proof that a
great deal of complexity can be centralized primarily to one place to
provide a more consistent and simple experience elsewhere.
### Using These Options
It doesn't matter if you're using a target-specific compiler and a simulator or
a native compiler. In either case, you've got a couple choices for configuring
these options:
1. Because these options are specified via C defines, you can pass most of these
options to your compiler through command line compiler flags. Even if you're
using an embedded target that forces you to use their overbearing IDE for all
configuration, there will be a place somewhere in your project to configure
defines for your compiler.
2. You can create a custom `unity_config.h` configuration file (present in your
toolchain's search paths). In this file, you will list definitions and macros
specific to your target. All you must do is define `UNITY_INCLUDE_CONFIG_H` and
Unity will rely on `unity_config.h` for any further definitions it may need.
## The Options
### Integer Types
If you've been a C developer for long, you probably already know that C's
concept of an integer varies from target to target. The C Standard has rules
about the `int` matching the register size of the target microprocessor. It has
rules about the `int` and how its size relates to other integer types. An `int`
on one target might be 16 bits while on another target it might be 64. There are
more specific types in compilers compliant with C99 or later, but that's
certainly not every compiler you are likely to encounter. Therefore, Unity has a
number of features for helping to adjust itself to match your required integer
sizes. It starts off by trying to do it automatically.
##### `UNITY_EXCLUDE_STDINT_H`
The first thing that Unity does to guess your types is check `stdint.h`.
This file includes defines like `UINT_MAX` that Unity can use to
learn a lot about your system. It's possible you don't want it to do this
(um. why not?) or (more likely) it's possible that your system doesn't
support `stdint.h`. If that's the case, you're going to want to define this.
That way, Unity will know to skip the inclusion of this file and you won't
be left with a compiler error.
_Example:_
#define UNITY_EXCLUDE_STDINT_H
##### `UNITY_EXCLUDE_LIMITS_H`
The second attempt to guess your types is to check `limits.h`. Some compilers
that don't support `stdint.h` could include `limits.h` instead. If you don't
want Unity to check this file either, define this to make it skip the inclusion.
_Example:_
#define UNITY_EXCLUDE_LIMITS_H
If you've disabled both of the automatic options above, you're going to have to
do the configuration yourself. Don't worry. Even this isn't too bad... there are
just a handful of defines that you are going to specify if you don't like the
defaults.
##### `UNITY_INT_WIDTH`
Define this to be the number of bits an `int` takes up on your system. The
default, if not autodetected, is 32 bits.
_Example:_
#define UNITY_INT_WIDTH 16
##### `UNITY_LONG_WIDTH`
Define this to be the number of bits a `long` takes up on your system. The
default, if not autodetected, is 32 bits. This is used to figure out what kind
of 64-bit support your system can handle. Does it need to specify a `long` or a
`long long` to get a 64-bit value. On 16-bit systems, this option is going to be
ignored.
_Example:_
#define UNITY_LONG_WIDTH 16
##### `UNITY_POINTER_WIDTH`
Define this to be the number of bits a pointer takes up on your system. The
default, if not autodetected, is 32-bits. If you're getting ugly compiler
warnings about casting from pointers, this is the one to look at.
_Example:_
#define UNITY_POINTER_WIDTH 64
##### `UNITY_SUPPORT_64`
Unity will automatically include 64-bit support if it auto-detects it, or if
your `int`, `long`, or pointer widths are greater than 32-bits. Define this to
enable 64-bit support if none of the other options already did it for you. There
can be a significant size and speed impact to enabling 64-bit support on small
targets, so don't define it if you don't need it.
_Example:_
#define UNITY_SUPPORT_64
### Floating Point Types
In the embedded world, it's not uncommon for targets to have no support for
floating point operations at all or to have support that is limited to only
single precision. We are able to guess integer sizes on the fly because integers
are always available in at least one size. Floating point, on the other hand, is
sometimes not available at all. Trying to include `float.h` on these platforms
would result in an error. This leaves manual configuration as the only option.
##### `UNITY_INCLUDE_FLOAT`
##### `UNITY_EXCLUDE_FLOAT`
##### `UNITY_INCLUDE_DOUBLE`
##### `UNITY_EXCLUDE_DOUBLE`
By default, Unity guesses that you will want single precision floating point
support, but not double precision. It's easy to change either of these using the
include and exclude options here. You may include neither, either, or both, as
suits your needs. For features that are enabled, the following floating point
options also become available.
_Example:_
//what manner of strange processor is this?
#define UNITY_EXCLUDE_FLOAT
#define UNITY_INCLUDE_DOUBLE
##### `UNITY_EXCLUDE_FLOAT_PRINT`
Unity aims for as small of a footprint as possible and avoids most standard
library calls (some embedded platforms dont have a standard library!). Because
of this, its routines for printing integer values are minimalist and hand-coded.
Therefore, the display of floating point values during a failure are optional.
By default, Unity will print the actual results of floating point assertion
failure (e.g. ”Expected 4.56 Was 4.68”). To not include this extra support, you
can use this define to instead respond to a failed assertion with a message like
”Values Not Within Delta”. If you would like verbose failure messages for floating
point assertions, use these options to give more explicit failure messages.
_Example:_
#define UNITY_EXCLUDE_FLOAT_PRINT
##### `UNITY_FLOAT_TYPE`
If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C
floats. If your compiler supports a specialty floating point type, you can
always override this behavior by using this definition.
_Example:_
#define UNITY_FLOAT_TYPE float16_t
##### `UNITY_DOUBLE_TYPE`
If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard C
doubles. If you would like to change this, you can specify something else by
using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long double`
could enable gargantuan floating point types on your 64-bit processor instead of
the standard `double`.
_Example:_
#define UNITY_DOUBLE_TYPE long double
##### `UNITY_FLOAT_PRECISION`
##### `UNITY_DOUBLE_PRECISION`
If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as
documented in the big daddy Unity Assertion Guide, you will learn that they are
not really asserting that two values are equal but rather that two values are
"close enough" to equal. "Close enough" is controlled by these precision
configuration options. If you are working with 32-bit floats and/or 64-bit
doubles (the normal on most processors), you should have no need to change these
options. They are both set to give you approximately 1 significant bit in either
direction. The float precision is 0.00001 while the double is 10-12.
For further details on how this works, see the appendix of the Unity Assertion
Guide.
_Example:_
#define UNITY_FLOAT_PRECISION 0.001f
### Toolset Customization
In addition to the options listed above, there are a number of other options
which will come in handy to customize Unity's behavior for your specific
toolchain. It is possible that you may not need to touch any of these... but
certain platforms, particularly those running in simulators, may need to jump
through extra hoops to run properly. These macros will help in those
situations.
##### `UNITY_OUTPUT_CHAR(a)`
##### `UNITY_OUTPUT_FLUSH()`
##### `UNITY_OUTPUT_START()`
##### `UNITY_OUTPUT_COMPLETE()`
By default, Unity prints its results to `stdout` as it runs. This works
perfectly fine in most situations where you are using a native compiler for
testing. It works on some simulators as well so long as they have `stdout`
routed back to the command line. There are times, however, where the simulator
will lack support for dumping results or you will want to route results
elsewhere for other reasons. In these cases, you should define the
`UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time (as
an `int`, since this is the parameter type of the standard C `putchar` function
most commonly used). You may replace this with whatever function call you like.
_Example:_
Say you are forced to run your test suite on an embedded processor with no
`stdout` option. You decide to route your test result output to a custom serial
`RS232_putc()` function you wrote like thus:
#include "RS232_header.h"
...
#define UNITY_OUTPUT_CHAR(a) RS232_putc(a)
#define UNITY_OUTPUT_START() RS232_config(115200,1,8,0)
#define UNITY_OUTPUT_FLUSH() RS232_flush()
#define UNITY_OUTPUT_COMPLETE() RS232_close()
_Note:_
`UNITY_OUTPUT_FLUSH()` can be set to the standard out flush function simply by
specifying `UNITY_USE_FLUSH_STDOUT`. No other defines are required.
##### `UNITY_WEAK_ATTRIBUTE`
##### `UNITY_WEAK_PRAGMA`
##### `UNITY_NO_WEAK`
For some targets, Unity can make the otherwise required setUp() and tearDown()
functions optional. This is a nice convenience for test writers since setUp and
tearDown dont often actually do anything. If youre using gcc or clang, this
option is automatically defined for you. Other compilers can also support this
behavior, if they support a C feature called weak functions. A weak function is
a function that is compiled into your executable unless a non-weak version of
the same function is defined elsewhere. If a non-weak version is found, the weak
version is ignored as if it never existed. If your compiler supports this feature,
you can let Unity know by defining UNITY_WEAK_ATTRIBUTE or UNITY_WEAK_PRAGMA as
the function attributes that would need to be applied to identify a function as
weak. If your compiler lacks support for weak functions, you will always need to
define setUp and tearDown functions (though they can be and often will be just
empty). You can also force Unity to NOT use weak functions by defining
UNITY_NO_WEAK. The most common options for this feature are:
_Example:_
#define UNITY_WEAK_ATTRIBUTE weak
#define UNITY_WEAK_ATTRIBUTE __attribute__((weak))
#define UNITY_WEAK_PRAGMA
#define UNITY_NO_WEAK
##### `UNITY_PTR_ATTRIBUTE`
Some compilers require a custom attribute to be assigned to pointers, like
`near` or `far`. In these cases, you can give Unity a safe default for these by
defining this option with the attribute you would like.
_Example:_
#define UNITY_PTR_ATTRIBUTE __attribute__((far))
#define UNITY_PTR_ATTRIBUTE near
##### `UNITY_PRINT_EOL`
By default, Unity outputs \n at the end of each line of output. This is easy
to parse by the scripts, by Ceedling, etc, but it might not be ideal for YOUR
system. Feel free to override this and to make it whatever you wish.
_Example:_
#define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n') }
##### `UNITY_EXCLUDE_DETAILS`
This is an option for if you absolutely must squeeze every byte of memory out of
your system. Unity stores a set of internal scratchpads which are used to pass
extra detail information around. It's used by systems like CMock in order to
report which function or argument flagged an error. If you're not using CMock and
you're not using these details for other things, then you can exclude them.
_Example:_
#define UNITY_EXCLUDE_DETAILS
##### `UNITY_EXCLUDE_SETJMP`
If your embedded system doesn't support the standard library setjmp, you can
exclude Unity's reliance on this by using this define. This dropped dependence
comes at a price, though. You will be unable to use custom helper functions for
your tests, and you will be unable to use tools like CMock. Very likely, if your
compiler doesn't support setjmp, you wouldn't have had the memory space for those
things anyway, though... so this option exists for those situations.
_Example:_
#define UNITY_EXCLUDE_SETJMP
##### `UNITY_OUTPUT_COLOR`
If you want to add color using ANSI escape codes you can use this define.
t
_Example:_
#define UNITY_OUTPUT_COLOR
## Getting Into The Guts
There will be cases where the options above aren't quite going to get everything
perfect. They are likely sufficient for any situation where you are compiling
and executing your tests with a native toolchain (e.g. clang on Mac). These
options may even get you through the majority of cases encountered in working
with a target simulator run from your local command line. But especially if you
must run your test suite on your target hardware, your Unity configuration will
require special help. This special help will usually reside in one of two
places: the `main()` function or the `RUN_TEST` macro. Let's look at how these
work.
##### `main()`
Each test module is compiled and run on its own, separate from the other test
files in your project. Each test file, therefore, has a `main` function. This
`main` function will need to contain whatever code is necessary to initialize
your system to a workable state. This is particularly true for situations where
you must set up a memory map or initialize a communication channel for the
output of your test results.
A simple main function looks something like this:
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_TheFirst);
RUN_TEST(test_TheSecond);
RUN_TEST(test_TheThird);
return UNITY_END();
}
You can see that our main function doesn't bother taking any arguments. For our
most barebones case, we'll never have arguments because we just run all the
tests each time. Instead, we start by calling `UNITY_BEGIN`. We run each test
(in whatever order we wish). Finally, we call `UNITY_END`, returning its return
value (which is the total number of failures).
It should be easy to see that you can add code before any test cases are run or
after all the test cases have completed. This allows you to do any needed
system-wide setup or teardown that might be required for your special
circumstances.
##### `RUN_TEST`
The `RUN_TEST` macro is called with each test case function. Its job is to
perform whatever setup and teardown is necessary for executing a single test
case function. This includes catching failures, calling the test module's
`setUp()` and `tearDown()` functions, and calling `UnityConcludeTest()`. If
using CMock or test coverage, there will be additional stubs in use here. A
simple minimalist RUN_TEST macro looks something like this:
#define RUN_TEST(testfunc) \
UNITY_NEW_TEST(#testfunc) \
if (TEST_PROTECT()) { \
setUp(); \
testfunc(); \
} \
if (TEST_PROTECT() && (!TEST_IS_IGNORED)) \
tearDown(); \
UnityConcludeTest();
So that's quite a macro, huh? It gives you a glimpse of what kind of stuff Unity
has to deal with for every single test case. For each test case, we declare that
it is a new test. Then we run `setUp` and our test function. These are run
within a `TEST_PROTECT` block, the function of which is to handle failures that
occur during the test. Then, assuming our test is still running and hasn't been
ignored, we run `tearDown`. No matter what, our last step is to conclude this
test before moving on to the next.
Let's say you need to add a call to `fsync` to force all of your output data to
flush to a file after each test. You could easily insert this after your
`UnityConcludeTest` call. Maybe you want to write an xml tag before and after
each result set. Again, you could do this by adding lines to this macro. Updates
to this macro are for the occasions when you need an action before or after
every single test case throughout your entire suite of tests.
## Happy Porting
The defines and macros in this guide should help you port Unity to just about
any C target we can imagine. If you run into a snag or two, don't be afraid of
asking for help on the forums. We love a good challenge!
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*

View File

@ -1,192 +0,0 @@
# Unity - Getting Started
## Welcome
Congratulations. You're now the proud owner of your very own pile of bits! What
are you going to do with all these ones and zeros? This document should be able
to help you decide just that.
Unity is a unit test framework. The goal has been to keep it small and
functional. The core Unity test framework is three files: a single C file and a
couple header files. These team up to provide functions and macros to make
testing easier.
Unity was designed to be cross-platform. It works hard to stick with C standards
while still providing support for the many embedded C compilers that bend the
rules. Unity has been used with many compilers, including GCC, IAR, Clang,
Green Hills, Microchip, and MS Visual Studio. It's not much work to get it to
work with a new target.
### Overview of the Documents
#### Unity Assertions reference
This document will guide you through all the assertion options provided by
Unity. This is going to be your unit testing bread and butter. You'll spend more
time with assertions than any other part of Unity.
#### Unity Assertions Cheat Sheet
This document contains an abridged summary of the assertions described in the
previous document. It's perfect for printing and referencing while you
familiarize yourself with Unity's options.
#### Unity Configuration Guide
This document is the one to reference when you are going to use Unity with a new
target or compiler. It'll guide you through the configuration options and will
help you customize your testing experience to meet your needs.
#### Unity Helper Scripts
This document describes the helper scripts that are available for simplifying
your testing workflow. It describes the collection of optional Ruby scripts
included in the auto directory of your Unity installation. Neither Ruby nor
these scripts are necessary for using Unity. They are provided as a convenience
for those who wish to use them.
#### Unity License
What's an open source project without a license file? This brief document
describes the terms you're agreeing to when you use this software. Basically, we
want it to be useful to you in whatever context you want to use it, but please
don't blame us if you run into problems.
### Overview of the Folders
If you have obtained Unity through Github or something similar, you might be
surprised by just how much stuff you suddenly have staring you in the face.
Don't worry, Unity itself is very small. The rest of it is just there to make
your life easier. You can ignore it or use it at your convenience. Here's an
overview of everything in the project.
- `src` - This is the code you care about! This folder contains a C file and two
header files. These three files _are_ Unity.
- `docs` - You're reading this document, so it's possible you have found your way
into this folder already. This is where all the handy documentation can be
found.
- `examples` - This contains a few examples of using Unity.
- `extras` - These are optional add ons to Unity that are not part of the core
project. If you've reached us through James Grenning's book, you're going to
want to look here.
- `test` - This is how Unity and its scripts are all tested. If you're just using
Unity, you'll likely never need to go in here. If you are the lucky team member
who gets to port Unity to a new toolchain, this is a good place to verify
everything is configured properly.
- `auto` - Here you will find helpful Ruby scripts for simplifying your test
workflow. They are purely optional and are not required to make use of Unity.
## How to Create A Test File
Test files are C files. Most often you will create a single test file for each C
module that you want to test. The test file should include unity.h and the
header for your C module to be tested.
Next, a test file will include a `setUp()` and `tearDown()` function. The setUp
function can contain anything you would like to run before each test. The
tearDown function can contain anything you would like to run after each test.
Both functions accept no arguments and return nothing. You may leave either or
both of these blank if you have no need for them. If you're using a compiler
that is configured to make these functions optional, you may leave them off
completely. Not sure? Give it a try. If you compiler complains that it can't
find setUp or tearDown when it links, you'll know you need to at least include
an empty function for these.
The majority of the file will be a series of test functions. Test functions
follow the convention of starting with the word "test_" or "spec_". You don't HAVE
to name them this way, but it makes it clear what functions are tests for other
developers. Also, the automated scripts that come with Unity or Ceedling will default
to looking for test functions to be prefixed this way. Test functions take no arguments
and return nothing. All test accounting is handled internally in Unity.
Finally, at the bottom of your test file, you will write a `main()` function.
This function will call `UNITY_BEGIN()`, then `RUN_TEST` for each test, and
finally `UNITY_END()`.This is what will actually trigger each of those test
functions to run, so it is important that each function gets its own `RUN_TEST`
call.
Remembering to add each test to the main function can get to be tedious. If you
enjoy using helper scripts in your build process, you might consider making use
of our handy generate_test_runner.rb script. This will create the main function
and all the calls for you, assuming that you have followed the suggested naming
conventions. In this case, there is no need for you to include the main function
in your test file at all.
When you're done, your test file will look something like this:
```C
#include "unity.h"
#include "file_to_test.h"
void setUp(void) {
// set stuff up here
}
void tearDown(void) {
// clean stuff up here
}
void test_function_should_doBlahAndBlah(void) {
//test stuff
}
void test_function_should_doAlsoDoBlah(void) {
//more test stuff
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_function_should_doBlahAndBlah);
RUN_TEST(test_function_should_doAlsoDoBlah);
return UNITY_END();
}
```
It's possible that you will need more customization than this, eventually.
For that sort of thing, you're going to want to look at the configuration guide.
This should be enough to get you going, though.
## How to Build and Run A Test File
This is the single biggest challenge to picking up a new unit testing framework,
at least in a language like C or C++. These languages are REALLY good at getting
you "close to the metal" (why is the phrase metal? Wouldn't it be more accurate
to say "close to the silicon"?). While this feature is usually a good thing, it
can make testing more challenging.
You have two really good options for toolchains. Depending on where you're
coming from, it might surprise you that neither of these options is running the
unit tests on your hardware.
There are many reasons for this, but here's a short version:
- On hardware, you have too many constraints (processing power, memory, etc),
- On hardware, you don't have complete control over all registers,
- On hardware, unit testing is more challenging,
- Unit testing isn't System testing. Keep them separate.
Instead of running your tests on your actual hardware, most developers choose to
develop them as native applications (using gcc or MSVC for example) or as
applications running on a simulator. Either is a good option. Native apps have
the advantages of being faster and easier to set up. Simulator apps have the
advantage of working with the same compiler as your target application. The
options for configuring these are discussed in the configuration guide.
To get either to work, you might need to make a few changes to the file
containing your register set (discussed later).
In either case, a test is built by linking unity, the test file, and the C
file(s) being tested. These files create an executable which can be run as the
test set for that module. Then, this process is repeated for the next test file.
This flexibility of separating tests into individual executables allows us to
much more thoroughly unit test our system and it keeps all the test code out of
our final release!
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*

View File

@ -1,260 +0,0 @@
# Unity Helper Scripts
## With a Little Help From Our Friends
Sometimes what it takes to be a really efficient C programmer is a little non-C.
The Unity project includes a couple of Ruby scripts for making your life just a tad
easier. They are completely optional. If you choose to use them, you'll need a
copy of Ruby, of course. Just install whatever the latest version is, and it is
likely to work. You can find Ruby at [ruby-lang.org](https://ruby-labg.org/).
### `generate_test_runner.rb`
Are you tired of creating your own `main` function in your test file? Do you
keep forgetting to add a `RUN_TEST` call when you add a new test case to your
suite? Do you want to use CMock or other fancy add-ons but don't want to figure
out how to create your own `RUN_TEST` macro?
Well then we have the perfect script for you!
The `generate_test_runner` script processes a given test file and automatically
creates a separate test runner file that includes ?main?to execute the test
cases within the scanned test file. All you do then is add the generated runner
to your list of files to be compiled and linked, and presto you're done!
This script searches your test file for void function signatures having a
function name beginning with "test" or "spec". It treats each of these
functions as a test case and builds up a test suite of them. For example, the
following includes three test cases:
```C
void testVerifyThatUnityIsAwesomeAndWillMakeYourLifeEasier(void)
{
ASSERT_TRUE(1);
}
void test_FunctionName_should_WorkProperlyAndReturn8(void) {
ASSERT_EQUAL_INT(8, FunctionName());
}
void spec_Function_should_DoWhatItIsSupposedToDo(void) {
ASSERT_NOT_NULL(Function(5));
}
```
You can run this script a couple of ways. The first is from the command line:
```Shell
ruby generate_test_runner.rb TestFile.c NameOfRunner.c
```
Alternatively, if you include only the test file parameter, the script will copy
the name of the test file and automatically append "_Runner" to the name of the
generated file. The example immediately below will create TestFile_Runner.c.
```Shell
ruby generate_test_runner.rb TestFile.c
```
You can also add a [YAML](http://www.yaml.org/) file to configure extra options.
Conveniently, this YAML file is of the same format as that used by Unity and
CMock. So if you are using YAML files already, you can simply pass the very same
file into the generator script.
```Shell
ruby generate_test_runner.rb TestFile.c my_config.yml
```
The contents of the YAML file `my_config.yml` could look something like the
example below. If you're wondering what some of these options do, you're going
to love the next section of this document.
```YAML
:unity:
:includes:
- stdio.h
- microdefs.h
:cexception: 1
:suit_setup: "blah = malloc(1024);"
:suite_teardown: "free(blah);"
```
If you would like to force your generated test runner to include one or more
header files, you can just include those at the command line too. Just make sure
these are _after_ the YAML file, if you are using one:
```Shell
ruby generate_test_runner.rb TestFile.c my_config.yml extras.h
```
Another option, particularly if you are already using Ruby to orchestrate your
builds - or more likely the Ruby-based build tool Rake - is requiring this
script directly. Anything that you would have specified in a YAML file can be
passed to the script as part of a hash. Let's push the exact same requirement
set as we did above but this time through Ruby code directly:
```Ruby
require "generate_test_runner.rb"
options = {
:includes => ["stdio.h", "microdefs.h"],
:cexception => 1,
:suite_setup => "blah = malloc(1024);",
:suite_teardown => "free(blah);"
}
UnityTestRunnerGenerator.new.run(testfile, runner_name, options)
```
If you have multiple files to generate in a build script (such as a Rakefile),
you might want to instantiate a generator object with your options and call it
to generate each runner afterwards. Like thus:
```Ruby
gen = UnityTestRunnerGenerator.new(options)
test_files.each do |f|
gen.run(f, File.basename(f,'.c')+"Runner.c"
end
```
#### Options accepted by generate_test_runner.rb:
The following options are available when executing `generate_test_runner`. You
may pass these as a Ruby hash directly or specify them in a YAML file, both of
which are described above. In the `examples` directory, Example 3's Rakefile
demonstrates using a Ruby hash.
##### `:includes`
This option specifies an array of file names to be `#include`'d at the top of
your runner C file. You might use it to reference custom types or anything else
universally needed in your generated runners.
##### `:suite_setup`
Define this option with C code to be executed _before any_ test cases are run.
Alternatively, if your C compiler supports weak symbols, you can leave this
option unset and instead provide a `void suiteSetUp(void)` function in your test
suite. The linker will look for this symbol and fall back to a Unity-provided
stub if it is not found.
##### `:suite_teardown`
Define this option with C code to be executed _after all_ test cases have
finished. An integer variable `num_failures` is available for diagnostics.
The code should end with a `return` statement; the value returned will become
the exit code of `main`. You can normally just return `num_failures`.
Alternatively, if your C compiler supports weak symbols, you can leave this
option unset and instead provide a `int suiteTearDown(int num_failures)`
function in your test suite. The linker will look for this symbol and fall
back to a Unity-provided stub if it is not found.
##### `:enforce_strict_ordering`
This option should be defined if you have the strict order feature enabled in
CMock (see CMock documentation). This generates extra variables required for
everything to run smoothly. If you provide the same YAML to the generator as
used in CMock's configuration, you've already configured the generator properly.
##### `:mock_prefix` and `:mock_suffix`
Unity automatically generates calls to Init, Verify and Destroy for every file
included in the main test file that starts with the given mock prefix and ends
with the given mock suffix, file extension not included. By default, Unity
assumes a `Mock` prefix and no suffix.
##### `:plugins`
This option specifies an array of plugins to be used (of course, the array can
contain only a single plugin). This is your opportunity to enable support for
CException support, which will add a check for unhandled exceptions in each
test, reporting a failure if one is detected. To enable this feature using Ruby:
```Ruby
:plugins => [ :cexception ]
```
Or as a yaml file:
```YAML
:plugins:
-:cexception
```
If you are using CMock, it is very likely that you are already passing an array
of plugins to CMock. You can just use the same array here. This script will just
ignore the plugins that don't require additional support.
### `unity_test_summary.rb`
A Unity test file contains one or more test case functions. Each test case can
pass, fail, or be ignored. Each test file is run individually producing results
for its collection of test cases. A given project will almost certainly be
composed of multiple test files. Therefore, the suite of tests is comprised of
one or more test cases spread across one or more test files. This script
aggregates individual test file results to generate a summary of all executed
test cases. The output includes how many tests were run, how many were ignored,
and how many failed. In addition, the output includes a listing of which
specific tests were ignored and failed. A good example of the breadth and
details of these results can be found in the `examples` directory. Intentionally
ignored and failing tests in this project generate corresponding entries in the
summary report.
If you're interested in other (prettier?) output formats, check into the
Ceedling build tool project (ceedling.sourceforge.net) that works with Unity and
CMock and supports xunit-style xml as well as other goodies.
This script assumes the existence of files ending with the extensions
`.testpass` and `.testfail`.The contents of these files includes the test
results summary corresponding to each test file executed with the extension set
according to the presence or absence of failures for that test file. The script
searches a specified path for these files, opens each one it finds, parses the
results, and aggregates and prints a summary. Calling it from the command line
looks like this:
```Shell
ruby unity_test_summary.rb build/test/
```
You can optionally specify a root path as well. This is really helpful when you
are using relative paths in your tools' setup, but you want to pull the summary
into an IDE like Eclipse for clickable shortcuts.
```Shell
ruby unity_test_summary.rb build/test/ ~/projects/myproject/
```
Or, if you're more of a Windows sort of person:
```Shell
ruby unity_test_summary.rb build\teat\ C:\projects\myproject\
```
When configured correctly, you'll see a final summary, like so:
```Shell
--------------------------
UNITY IGNORED TEST SUMMARY
--------------------------
blah.c:22:test_sandwiches_should_HaveBreadOnTwoSides:IGNORE
-------------------------
UNITY FAILED TEST SUMMARY
-------------------------
blah.c:87:test_sandwiches_should_HaveCondiments:FAIL:Expected 1 was 0
meh.c:38:test_soda_should_BeCalledPop:FAIL:Expected "pop" was "coke"
--------------------------
OVERALL UNITY TEST SUMMARY
--------------------------
45 TOTAL TESTS 2 TOTAL FAILURES 1 IGNORED
```
How convenient is that?
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*

View File

@ -9,7 +9,7 @@ class BuildInvokerUtils
## ##
# Processes exceptions and tries to display a useful message for the user. # Processes exceptions and tries to display a useful message for the user.
# #
# ==== Attriboops...utes # ==== Attributes
# #
# * _exception_: The exception given by a rescue statement. # * _exception_: The exception given by a rescue statement.
# * _context_: A symbol representing where in the build the exception # * _context_: A symbol representing where in the build the exception

View File

@ -4,28 +4,32 @@ class CacheinatorHelper
constructor :file_wrapper, :yaml_wrapper constructor :file_wrapper, :yaml_wrapper
def diff_cached_config?(cached_filepath, hash) def diff_cached_config?(cached_filepath, hash)
return true if ( not @file_wrapper.exist?(cached_filepath) ) return false if ( not @file_wrapper.exist?(cached_filepath) )
return true if ( (@file_wrapper.exist?(cached_filepath)) and (!(@yaml_wrapper.load(cached_filepath) == hash)) ) return true if (@yaml_wrapper.load(cached_filepath) != hash)
return false return false
end end
def diff_cached_defines?(cached_filepath, files) def diff_cached_defines?(cached_filepath, files)
changed_defines = false
current_defines = COLLECTION_DEFINES_TEST_AND_VENDOR.reject(&:empty?) current_defines = COLLECTION_DEFINES_TEST_AND_VENDOR.reject(&:empty?)
current_dependency = Hash[files.collect { |source| [source, current_defines.dup] }] current_dependencies = Hash[files.collect { |source| [source, current_defines.dup] }]
if not @file_wrapper.exist?(cached_filepath) if not @file_wrapper.exist?(cached_filepath)
@yaml_wrapper.dump(cached_filepath, current_dependency) @yaml_wrapper.dump(cached_filepath, current_dependencies)
return false return changed_defines
end end
dependencies = @yaml_wrapper.load(cached_filepath) dependencies = @yaml_wrapper.load(cached_filepath)
if dependencies.values_at(*current_dependency.keys) != current_dependency.values common_dependencies = current_dependencies.select { |file, defines| dependencies.has_key?(file) }
dependencies.merge!(current_dependency)
@yaml_wrapper.dump(cached_filepath, dependencies) if dependencies.values_at(*common_dependencies.keys) != common_dependencies.values
return true changed_defines = true
end end
return false dependencies.merge!(current_dependencies)
@yaml_wrapper.dump(cached_filepath, dependencies)
return changed_defines
end end
end end

View File

@ -54,6 +54,7 @@ class Configurator
:test_fixture, :test_fixture,
:test_includes_preprocessor, :test_includes_preprocessor,
:test_file_preprocessor, :test_file_preprocessor,
:test_file_preprocessor_directives,
:test_dependencies_generator, :test_dependencies_generator,
:release_compiler, :release_compiler,
:release_assembler, :release_assembler,
@ -183,17 +184,22 @@ class Configurator
@rake_plugins = @configurator_plugins.find_rake_plugins(config, paths_hash) @rake_plugins = @configurator_plugins.find_rake_plugins(config, paths_hash)
@script_plugins = @configurator_plugins.find_script_plugins(config, paths_hash) @script_plugins = @configurator_plugins.find_script_plugins(config, paths_hash)
config_plugins = @configurator_plugins.find_config_plugins(config, paths_hash) config_plugins = @configurator_plugins.find_config_plugins(config, paths_hash)
plugin_defaults = @configurator_plugins.find_plugin_defaults(config, paths_hash) plugin_yml_defaults = @configurator_plugins.find_plugin_yml_defaults(config, paths_hash)
plugin_hash_defaults = @configurator_plugins.find_plugin_hash_defaults(config, paths_hash)
config_plugins.each do |plugin| config_plugins.each do |plugin|
plugin_config = @yaml_wrapper.load(plugin) plugin_config = @yaml_wrapper.load(plugin)
config.deep_merge(plugin_config) config.deep_merge(plugin_config)
end end
plugin_defaults.each do |defaults| plugin_yml_defaults.each do |defaults|
@configurator_builder.populate_defaults( config, @yaml_wrapper.load(defaults) ) @configurator_builder.populate_defaults( config, @yaml_wrapper.load(defaults) )
end end
plugin_hash_defaults.each do |defaults|
@configurator_builder.populate_defaults( config, defaults )
end
# special plugin setting for results printing # special plugin setting for results printing
config[:plugins][:display_raw_test_results] = true if (config[:plugins][:display_raw_test_results].nil?) config[:plugins][:display_raw_test_results] = true if (config[:plugins][:display_raw_test_results].nil?)
@ -203,10 +209,19 @@ class Configurator
def merge_imports(config) def merge_imports(config)
if config[:import] if config[:import]
until config[:import].empty? if config[:import].is_a? Array
path = config[:import].shift until config[:import].empty?
path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN) path = config[:import].shift
config.deep_merge!(@yaml_wrapper.load(path)) path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN)
config.deep_merge!(@yaml_wrapper.load(path))
end
else
config[:import].each_value do |path|
if !path.nil?
path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN)
config.deep_merge!(@yaml_wrapper.load(path))
end
end
end end
end end
config.delete(:import) config.delete(:import)
@ -222,7 +237,11 @@ class Configurator
interstitial = ((key == :path) ? File::PATH_SEPARATOR : '') interstitial = ((key == :path) ? File::PATH_SEPARATOR : '')
items = ((value.class == Array) ? hash[key] : [value]) items = ((value.class == Array) ? hash[key] : [value])
items.each { |item| item.replace( @system_wrapper.module_eval( item ) ) if (item =~ RUBY_STRING_REPLACEMENT_PATTERN) } items.each do |item|
if item.is_a? String and item =~ RUBY_STRING_REPLACEMENT_PATTERN
item.replace( @system_wrapper.module_eval( item ) )
end
end
hash[key] = items.join( interstitial ) hash[key] = items.join( interstitial )
@system_wrapper.env_set( key.to_s.upcase, hash[key] ) @system_wrapper.env_set( key.to_s.upcase, hash[key] )

View File

@ -250,8 +250,8 @@ class ConfiguratorBuilder
def collect_test_support_source_include_vendor_paths(in_hash) def collect_test_support_source_include_vendor_paths(in_hash)
return { return {
:collection_paths_test_support_source_include_vendor => :collection_paths_test_support_source_include_vendor =>
in_hash[:collection_paths_test_support_source_include] + get_vendor_paths(in_hash) +
get_vendor_paths(in_hash) in_hash[:collection_paths_test_support_source_include]
} }
end end
@ -384,14 +384,26 @@ class ConfiguratorBuilder
end end
def get_vendor_defines(in_hash)
defines = in_hash[:unity_defines].clone
defines.concat(in_hash[:cmock_defines]) if (in_hash[:project_use_mocks])
defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions])
return defines
end
def collect_vendor_defines(in_hash)
return {:collection_defines_vendor => get_vendor_defines(in_hash)}
end
def collect_test_and_vendor_defines(in_hash) def collect_test_and_vendor_defines(in_hash)
test_defines = in_hash[:defines_test].clone defines = in_hash[:defines_test].clone
vendor_defines = get_vendor_defines(in_hash)
defines.concat(vendor_defines) if vendor_defines
test_defines.concat(in_hash[:unity_defines]) return {:collection_defines_test_and_vendor => defines}
test_defines.concat(in_hash[:cmock_defines]) if (in_hash[:project_use_mocks])
test_defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions])
return {:collection_defines_test_and_vendor => test_defines}
end end
@ -418,28 +430,33 @@ class ConfiguratorBuilder
# Note: Symbols passed to compiler at command line can change Unity and CException behavior / configuration; # Note: Symbols passed to compiler at command line can change Unity and CException behavior / configuration;
# we also handle those dependencies elsewhere in compilation dependencies # we also handle those dependencies elsewhere in compilation dependencies
objects = [UNITY_C_FILE] sources = [UNITY_C_FILE]
in_hash[:files_support].each { |file| objects << File.basename(file) } in_hash[:files_support].each { |file| sources << file }
# we don't include paths here because use of plugins or mixing different compilers may require different build paths # we don't include paths here because use of plugins or mixing different compilers may require different build paths
objects << CEXCEPTION_C_FILE if (in_hash[:project_use_exceptions]) sources << CEXCEPTION_C_FILE if (in_hash[:project_use_exceptions])
objects << CMOCK_C_FILE if (in_hash[:project_use_mocks]) sources << CMOCK_C_FILE if (in_hash[:project_use_mocks])
# if we're using mocks & a unity helper is defined & that unity helper includes a source file component (not only a header of macros), # if we're using mocks & a unity helper is defined & that unity helper includes a source file component (not only a header of macros),
# then link in the unity_helper object file too # then link in the unity_helper object file too
if ( in_hash[:project_use_mocks] and in_hash[:cmock_unity_helper] ) if ( in_hash[:project_use_mocks] and in_hash[:cmock_unity_helper] )
in_hash[:cmock_unity_helper].each do |helper| in_hash[:cmock_unity_helper].each do |helper|
if @file_wrapper.exist?(helper.ext(in_hash[:extension_source])) if @file_wrapper.exist?(helper.ext(in_hash[:extension_source]))
objects << File.basename(helper) sources << helper
end end
end end
end end
# create object files from all the sources
objects = sources.map { |file| File.basename(file) }
# no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime) # no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime)
objects.map! { |object| object.ext(in_hash[:extension_object]) } objects.map! { |object| object.ext(in_hash[:extension_object]) }
return { :collection_test_fixture_extra_link_objects => objects } return { :collection_all_support => sources,
:collection_test_fixture_extra_link_objects => objects
}
end end

View File

@ -26,6 +26,7 @@ class ConfiguratorPlugins
if is_script_plugin if is_script_plugin
@system_wrapper.add_load_path( File.join( path, 'lib') ) @system_wrapper.add_load_path( File.join( path, 'lib') )
@system_wrapper.add_load_path( File.join( path, 'config') )
end end
break break
end end
@ -92,7 +93,7 @@ class ConfiguratorPlugins
# gather up and return default .yml filepaths that exist on-disk # gather up and return default .yml filepaths that exist on-disk
def find_plugin_defaults(config, plugin_paths) def find_plugin_yml_defaults(config, plugin_paths)
defaults_with_path = [] defaults_with_path = []
config[:plugins][:enabled].each do |plugin| config[:plugins][:enabled].each do |plugin|
@ -108,4 +109,23 @@ class ConfiguratorPlugins
return defaults_with_path return defaults_with_path
end end
# gather up and return
def find_plugin_hash_defaults(config, plugin_paths)
defaults_hash= []
config[:plugins][:enabled].each do |plugin|
if path = plugin_paths[(plugin + '_path').to_sym]
default_path = File.join(path, "config", "defaults_#{plugin}.rb")
if @file_wrapper.exist?(default_path)
@system_wrapper.require_file( "defaults_#{plugin}.rb")
object = eval("get_default_config()")
defaults_hash << object
end
end
end
return defaults_hash
end
end end

View File

@ -39,6 +39,7 @@ class ConfiguratorSetup
flattened_config.merge!(@configurator_builder.collect_headers(flattened_config)) flattened_config.merge!(@configurator_builder.collect_headers(flattened_config))
flattened_config.merge!(@configurator_builder.collect_release_existing_compilation_input(flattened_config)) flattened_config.merge!(@configurator_builder.collect_release_existing_compilation_input(flattened_config))
flattened_config.merge!(@configurator_builder.collect_all_existing_compilation_input(flattened_config)) flattened_config.merge!(@configurator_builder.collect_all_existing_compilation_input(flattened_config))
flattened_config.merge!(@configurator_builder.collect_vendor_defines(flattened_config))
flattened_config.merge!(@configurator_builder.collect_test_and_vendor_defines(flattened_config)) flattened_config.merge!(@configurator_builder.collect_test_and_vendor_defines(flattened_config))
flattened_config.merge!(@configurator_builder.collect_release_and_vendor_defines(flattened_config)) flattened_config.merge!(@configurator_builder.collect_release_and_vendor_defines(flattened_config))
flattened_config.merge!(@configurator_builder.collect_release_artifact_extra_link_objects(flattened_config)) flattened_config.merge!(@configurator_builder.collect_release_artifact_extra_link_objects(flattened_config))

View File

@ -95,3 +95,5 @@ NULL_FILE_PATH = '/dev/null'
TESTS_BASE_PATH = TEST_ROOT_NAME TESTS_BASE_PATH = TEST_ROOT_NAME
RELEASE_BASE_PATH = RELEASE_ROOT_NAME RELEASE_BASE_PATH = RELEASE_ROOT_NAME
VENDORS_FILES = %w(unity UnityHelper cmock CException).freeze

View File

@ -7,17 +7,20 @@ CEEDLING_VENDOR = File.expand_path(File.dirname(__FILE__) + '/../../vendor') unl
CEEDLING_PLUGINS = [] unless defined? CEEDLING_PLUGINS CEEDLING_PLUGINS = [] unless defined? CEEDLING_PLUGINS
DEFAULT_TEST_COMPILER_TOOL = { DEFAULT_TEST_COMPILER_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0],
:name => 'default_test_compiler'.freeze, :name => 'default_test_compiler'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1],
ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
"-DGNU_COMPILER".freeze, "-DGNU_COMPILER".freeze,
"-g".freeze, "-g".freeze,
ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split,
"-c \"${1}\"".freeze, "-c \"${1}\"".freeze,
"-o \"${2}\"".freeze, "-o \"${2}\"".freeze,
# gcc's list file output options are complex; no use of ${3} parameter in default config # gcc's list file output options are complex; no use of ${3} parameter in default config
@ -27,16 +30,21 @@ DEFAULT_TEST_COMPILER_TOOL = {
} }
DEFAULT_TEST_LINKER_TOOL = { DEFAULT_TEST_LINKER_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0],
:name => 'default_test_linker'.freeze, :name => 'default_test_linker'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1],
ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split,
ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split,
"\"${1}\"".freeze, "\"${1}\"".freeze,
"${5}".freeze,
"-o \"${2}\"".freeze, "-o \"${2}\"".freeze,
"".freeze, "".freeze,
"${4}".freeze "${4}".freeze,
ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split
].freeze ].freeze
} }
@ -50,12 +58,14 @@ DEFAULT_TEST_FIXTURE_TOOL = {
} }
DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = { DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0],
:name => 'default_test_includes_preprocessor'.freeze, :name => 'default_test_includes_preprocessor'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1],
ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split,
'-E'.freeze, # OSX clang '-E'.freeze, # OSX clang
'-MM'.freeze, '-MM'.freeze,
'-MG'.freeze, '-MG'.freeze,
@ -67,18 +77,38 @@ DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = {
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
{"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
"-DGNU_COMPILER".freeze, # OSX clang "-DGNU_COMPILER".freeze, # OSX clang
'-w'.freeze,
# '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX
"\"${1}\"".freeze "\"${1}\"".freeze
].freeze ].freeze
} }
DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = { DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0],
:name => 'default_test_file_preprocessor'.freeze, :name => 'default_test_file_preprocessor'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [
ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1],
ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split,
'-E'.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
{"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
"-DGNU_COMPILER".freeze,
# '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX
"\"${1}\"".freeze,
"-o \"${2}\"".freeze
].freeze
}
DEFAULT_TEST_FILE_PREPROCESSOR_DIRECTIVES_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze,
:name => 'default_test_file_preprocessor_directives'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze,
:arguments => [ :arguments => [
'-E'.freeze, '-E'.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
@ -86,6 +116,7 @@ DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = {
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
{"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze, {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
"-DGNU_COMPILER".freeze, "-DGNU_COMPILER".freeze,
'-fdirectives-only'.freeze,
# '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX # '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX
"\"${1}\"".freeze, "\"${1}\"".freeze,
"-o \"${2}\"".freeze "-o \"${2}\"".freeze
@ -100,12 +131,14 @@ else
end end
DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = { DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0],
:name => 'default_test_dependencies_generator'.freeze, :name => 'default_test_dependencies_generator'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1],
ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split,
'-E'.freeze, '-E'.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
@ -123,12 +156,14 @@ DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = {
} }
DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = { DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0],
:name => 'default_release_dependencies_generator'.freeze, :name => 'default_release_dependencies_generator'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1],
ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split,
'-E'.freeze, '-E'.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze,
@ -147,16 +182,19 @@ DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = {
DEFAULT_RELEASE_COMPILER_TOOL = { DEFAULT_RELEASE_COMPILER_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0],
:name => 'default_release_compiler'.freeze, :name => 'default_release_compiler'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1],
ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split,
{"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze,
{"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze, {"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze,
"-DGNU_COMPILER".freeze, "-DGNU_COMPILER".freeze,
ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split,
"-c \"${1}\"".freeze, "-c \"${1}\"".freeze,
"-o \"${2}\"".freeze, "-o \"${2}\"".freeze,
# gcc's list file output options are complex; no use of ${3} parameter in default config # gcc's list file output options are complex; no use of ${3} parameter in default config
@ -166,12 +204,14 @@ DEFAULT_RELEASE_COMPILER_TOOL = {
} }
DEFAULT_RELEASE_ASSEMBLER_TOOL = { DEFAULT_RELEASE_ASSEMBLER_TOOL = {
:executable => FilePathUtils.os_executable_ext('as').freeze, :executable => ENV['AS'].nil? ? FilePathUtils.os_executable_ext('as').freeze : ENV['AS'].split[0],
:name => 'default_release_assembler'.freeze, :name => 'default_release_assembler'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['AS'].nil? ? "" : ENV['AS'].split[1..-1],
ENV['ASFLAGS'].nil? ? "" : ENV['ASFLAGS'].split,
{"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_AND_INCLUDE'}.freeze, {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_AND_INCLUDE'}.freeze,
"\"${1}\"".freeze, "\"${1}\"".freeze,
"-o \"${2}\"".freeze, "-o \"${2}\"".freeze,
@ -179,16 +219,21 @@ DEFAULT_RELEASE_ASSEMBLER_TOOL = {
} }
DEFAULT_RELEASE_LINKER_TOOL = { DEFAULT_RELEASE_LINKER_TOOL = {
:executable => FilePathUtils.os_executable_ext('gcc').freeze, :executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0],
:name => 'default_release_linker'.freeze, :name => 'default_release_linker'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze, :stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze, :background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze, :optional => false.freeze,
:arguments => [ :arguments => [
ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1],
ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split,
ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split,
"\"${1}\"".freeze, "\"${1}\"".freeze,
"${5}".freeze,
"-o \"${2}\"".freeze, "-o \"${2}\"".freeze,
"".freeze, "".freeze,
"${4}".freeze "${4}".freeze,
ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split
].freeze ].freeze
} }
@ -205,6 +250,7 @@ DEFAULT_TOOLS_TEST_PREPROCESSORS = {
:tools => { :tools => {
:test_includes_preprocessor => DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL, :test_includes_preprocessor => DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL,
:test_file_preprocessor => DEFAULT_TEST_FILE_PREPROCESSOR_TOOL, :test_file_preprocessor => DEFAULT_TEST_FILE_PREPROCESSOR_TOOL,
:test_file_preprocessor_directives => DEFAULT_TEST_FILE_PREPROCESSOR_DIRECTIVES_TOOL,
} }
} }
@ -245,8 +291,10 @@ DEFAULT_CEEDLING_CONFIG = {
:compile_threads => 1, :compile_threads => 1,
:test_threads => 1, :test_threads => 1,
:use_test_preprocessor => false, :use_test_preprocessor => false,
:use_preprocessor_directives => false,
:use_deep_dependencies => false, :use_deep_dependencies => false,
:generate_deep_dependencies => true, # only applicable if use_deep_dependencies is true :generate_deep_dependencies => true, # only applicable if use_deep_dependencies is true
:auto_link_deep_dependencies => false,
:test_file_prefix => 'test_', :test_file_prefix => 'test_',
:options_paths => [], :options_paths => [],
:release_build => false, :release_build => false,
@ -263,6 +311,7 @@ DEFAULT_CEEDLING_CONFIG = {
:source => [], # must be populated by user :source => [], # must be populated by user
:support => [], :support => [],
:include => [], :include => [],
:libraries => [],
:test_toolchain_include => [], :test_toolchain_include => [],
:release_toolchain_include => [], :release_toolchain_include => [],
}, },
@ -290,6 +339,8 @@ DEFAULT_CEEDLING_CONFIG = {
}, },
:libraries => { :libraries => {
:flag => '-l${1}',
:path_flag => '-L ${1}',
:test => [], :test => [],
:test_preprocess => [], :test_preprocess => [],
:release => [], :release => [],
@ -303,6 +354,7 @@ DEFAULT_CEEDLING_CONFIG = {
:source => '.c', :source => '.c',
:assembly => '.s', :assembly => '.s',
:object => '.o', :object => '.o',
:libraries => ['.a','.so'],
:executable => ( SystemWrapper.windows? ? EXTENSION_WIN_EXE : EXTENSION_NONWIN_EXE ), :executable => ( SystemWrapper.windows? ? EXTENSION_WIN_EXE : EXTENSION_NONWIN_EXE ),
:map => '.map', :map => '.map',
:list => '.lst', :list => '.lst',
@ -345,6 +397,7 @@ DEFAULT_CEEDLING_CONFIG = {
}, },
:test_includes_preprocessor => { :arguments => [] }, :test_includes_preprocessor => { :arguments => [] },
:test_file_preprocessor => { :arguments => [] }, :test_file_preprocessor => { :arguments => [] },
:test_file_preprocessor_directives => { :arguments => [] },
:test_dependencies_generator => { :arguments => [] }, :test_dependencies_generator => { :arguments => [] },
:release_compiler => { :arguments => [] }, :release_compiler => { :arguments => [] },
:release_linker => { :arguments => [] }, :release_linker => { :arguments => [] },

View File

@ -86,13 +86,12 @@ class Dependinator
def enhance_results_dependencies(result_filepath) def enhance_results_dependencies(result_filepath)
@rake_wrapper[result_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed || @rake_wrapper[result_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if @project_config_manager.test_config_changed
@project_config_manager.test_defines_changed)
end end
def setup_test_executable_dependencies(test, objects) def enhance_test_executable_dependencies(test, objects)
@rake_wrapper.create_file_task( @file_path_utils.form_test_executable_filepath(test), objects ) @rake_wrapper[ @file_path_utils.form_test_executable_filepath(test) ].enhance( objects )
end end
end end

View File

@ -25,10 +25,12 @@ class FileFinderHelper
end end
case (complain) if file_to_find.nil?
when :error then blow_up(file_name, extra_message) if (file_to_find.nil?) case (complain)
when :warn then gripe(file_name, extra_message) if (file_to_find.nil?) when :error then blow_up(file_name, extra_message)
#when :ignore then when :warn then gripe(file_name, extra_message)
#when :ignore then
end
end end
return file_to_find return file_to_find

View File

@ -21,9 +21,11 @@ class FilePathUtils
# standardize path to use '/' path separator & have no trailing path separator # standardize path to use '/' path separator & have no trailing path separator
def self.standardize(path) def self.standardize(path)
path.strip! if path.is_a? String
path.gsub!(/\\/, '/') path.strip!
path.chomp!('/') path.gsub!(/\\/, '/')
path.chomp!('/')
end
return path return path
end end

View File

@ -33,15 +33,15 @@ class FileWrapper
end end
def rm_f(filepath, options={}) def rm_f(filepath, options={})
FileUtils.rm_f(filepath, options) FileUtils.rm_f(filepath, **options)
end end
def rm_r(filepath, options={}) def rm_r(filepath, options={})
FileUtils.rm_r(filepath, options={}) FileUtils.rm_r(filepath, **options={})
end end
def cp(source, destination, options={}) def cp(source, destination, options={})
FileUtils.cp(source, destination, options) FileUtils.cp(source, destination, **options)
end end
def compare(from, to) def compare(from, to)
@ -59,7 +59,7 @@ class FileWrapper
end end
def touch(filepath, options={}) def touch(filepath, options={})
FileUtils.touch(filepath, options) FileUtils.touch(filepath, **options)
end end
def write(filepath, contents, flags='w') def write(filepath, contents, flags='w')

View File

@ -101,19 +101,21 @@ class Generator
shell_result = ex.shell_result shell_result = ex.shell_result
raise ex raise ex
ensure ensure
arg_hash[:shell_command] = command[:line]
arg_hash[:shell_result] = shell_result arg_hash[:shell_result] = shell_result
@plugin_manager.post_compile_execute(arg_hash) @plugin_manager.post_compile_execute(arg_hash)
end end
end end
def generate_executable_file(tool, context, objects, executable, map='', libraries=[]) def generate_executable_file(tool, context, objects, executable, map='', libraries=[], libpaths=[])
shell_result = {} shell_result = {}
arg_hash = { :tool => tool, arg_hash = { :tool => tool,
:context => context, :context => context,
:objects => objects, :objects => objects,
:executable => executable, :executable => executable,
:map => map, :map => map,
:libraries => libraries :libraries => libraries,
:libpaths => libpaths
} }
@plugin_manager.pre_link_execute(arg_hash) @plugin_manager.pre_link_execute(arg_hash)
@ -125,7 +127,8 @@ class Generator
arg_hash[:objects], arg_hash[:objects],
arg_hash[:executable], arg_hash[:executable],
arg_hash[:map], arg_hash[:map],
arg_hash[:libraries] arg_hash[:libraries],
arg_hash[:libpaths]
) )
@streaminator.stdout_puts("Command: #{command}", Verbosity::DEBUG) @streaminator.stdout_puts("Command: #{command}", Verbosity::DEBUG)

View File

@ -37,6 +37,10 @@ class GeneratorTestResults
elements = extract_line_elements(line, results[:source][:file]) elements = extract_line_elements(line, results[:source][:file])
results[:successes] << elements[0] results[:successes] << elements[0]
results[:stdout] << elements[1] if (!elements[1].nil?) results[:stdout] << elements[1] if (!elements[1].nil?)
when /(:PASS \(.* ms\)$)/
elements = extract_line_elements(line, results[:source][:file])
results[:successes] << elements[0]
results[:stdout] << elements[1] if (!elements[1].nil?)
when /(:FAIL)/ when /(:FAIL)/
elements = extract_line_elements(line, results[:source][:file]) elements = extract_line_elements(line, results[:source][:file])
results[:failures] << elements[0] results[:failures] << elements[0]
@ -73,6 +77,7 @@ class GeneratorTestResults
# handle anything preceding filename in line as extra output to be collected # handle anything preceding filename in line as extra output to be collected
stdout = nil stdout = nil
stdout_regex = /(.+)#{Regexp.escape(filename)}.+/i stdout_regex = /(.+)#{Regexp.escape(filename)}.+/i
unity_test_time = 0
if (line =~ stdout_regex) if (line =~ stdout_regex)
stdout = $1.clone stdout = $1.clone
@ -82,8 +87,14 @@ class GeneratorTestResults
# collect up test results minus and extra output # collect up test results minus and extra output
elements = (line.strip.split(':'))[1..-1] elements = (line.strip.split(':'))[1..-1]
return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip}, stdout if elements.size >= 3 # find timestamp if available
return {:test => '???', :line => -1, :message => nil} #fallback safe option. TODO better handling if (elements[-1] =~ / \((\d*(?:\.\d*)?) ms\)/)
unity_test_time = $1.to_f / 1000
elements[-1].sub!(/ \((\d*(?:\.\d*)?) ms\)/, '')
end
return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip, :unity_test_time => unity_test_time}, stdout if elements.size >= 3
return {:test => '???', :line => -1, :message => nil, :unity_test_time => unity_test_time} #fallback safe option. TODO better handling
end end
end end

View File

@ -44,13 +44,15 @@ class GeneratorTestRunner
def generate(module_name, runner_filepath, test_cases, mock_list, test_file_includes=[]) def generate(module_name, runner_filepath, test_cases, mock_list, test_file_includes=[])
require 'generate_test_runner.rb' require 'generate_test_runner.rb'
header_extension = @configurator.extension_header
#actually build the test runner using Unity's test runner generator #actually build the test runner using Unity's test runner generator
#(there is no need to use preprocessor here because we've already looked up test cases and are passing them in here) #(there is no need to use preprocessor here because we've already looked up test cases and are passing them in here)
@test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config ) @test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config )
@test_runner_generator.generate( module_name, @test_runner_generator.generate( module_name,
runner_filepath, runner_filepath,
test_cases, test_cases,
mock_list, mock_list.map{|f| File.basename(f,'.*')+header_extension},
test_file_includes) test_file_includes.map{|f| File.basename(f,'.*')+header_extension})
end end
end end

View File

@ -17,11 +17,11 @@ reportinator:
rake_utils: rake_utils:
compose: compose:
- rake_wrapper - rake_wrapper
system_utils: system_utils:
compose: compose:
- system_wrapper - system_wrapper
file_path_utils: file_path_utils:
compose: compose:
@ -203,13 +203,13 @@ generator_helper:
generator_test_results: generator_test_results:
compose: compose:
- configurator - configurator
- generator_test_results_sanity_checker - generator_test_results_sanity_checker
- yaml_wrapper - yaml_wrapper
generator_test_results_sanity_checker: generator_test_results_sanity_checker:
compose: compose:
- configurator - configurator
- streaminator - streaminator
generator_test_runner: generator_test_runner:
@ -223,43 +223,46 @@ dependinator:
- configurator - configurator
- project_config_manager - project_config_manager
- test_includes_extractor - test_includes_extractor
- file_path_utils - file_path_utils
- rake_wrapper - rake_wrapper
- file_wrapper - file_wrapper
preprocessinator: preprocessinator:
compose: compose:
- preprocessinator_helper - preprocessinator_helper
- preprocessinator_includes_handler - preprocessinator_includes_handler
- preprocessinator_file_handler - preprocessinator_file_handler
- task_invoker - task_invoker
- file_path_utils - file_path_utils
- yaml_wrapper - yaml_wrapper
- project_config_manager
- configurator
preprocessinator_helper: preprocessinator_helper:
compose: compose:
- configurator - configurator
- test_includes_extractor - test_includes_extractor
- task_invoker - task_invoker
- file_finder - file_finder
- file_path_utils - file_path_utils
preprocessinator_includes_handler: preprocessinator_includes_handler:
compose: compose:
- configurator - configurator
- tool_executor - tool_executor
- task_invoker - task_invoker
- file_path_utils - file_path_utils
- yaml_wrapper - yaml_wrapper
- file_wrapper - file_wrapper
- file_finder
preprocessinator_file_handler: preprocessinator_file_handler:
compose: compose:
- preprocessinator_extractor - preprocessinator_extractor
- configurator - configurator
- tool_executor - tool_executor
- file_path_utils - file_path_utils
- file_wrapper - file_wrapper
preprocessinator_extractor: preprocessinator_extractor:

View File

@ -1,29 +1,38 @@
class Preprocessinator class Preprocessinator
attr_reader :preprocess_file_proc constructor :preprocessinator_helper, :preprocessinator_includes_handler, :preprocessinator_file_handler, :task_invoker, :file_path_utils, :yaml_wrapper, :project_config_manager, :configurator
constructor :preprocessinator_helper, :preprocessinator_includes_handler, :preprocessinator_file_handler, :task_invoker, :file_path_utils, :yaml_wrapper
def setup def setup
# fashion ourselves callbacks @preprocessinator_helper can use # fashion ourselves callbacks @preprocessinator_helper can use
@preprocess_includes_proc = Proc.new { |filepath| self.preprocess_shallow_includes(filepath) } @preprocess_includes_proc = Proc.new { |filepath| self.preprocess_shallow_includes(filepath) }
@preprocess_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) } @preprocess_mock_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) }
@preprocess_test_file_directives_proc = Proc.new { |filepath| self.preprocess_file_directives(filepath) }
@preprocess_test_file_proc = Proc.new { |filepath| self.preprocess_file(filepath) }
end end
def preprocess_shallow_source_includes(test)
@preprocessinator_helper.preprocess_source_includes(test)
end
def preprocess_test_and_invoke_test_mocks(test) def preprocess_test_and_invoke_test_mocks(test)
@preprocessinator_helper.preprocess_includes(test, @preprocess_includes_proc) @preprocessinator_helper.preprocess_includes(test, @preprocess_includes_proc)
mocks_list = @preprocessinator_helper.assemble_mocks_list(test) mocks_list = @preprocessinator_helper.assemble_mocks_list(test)
@preprocessinator_helper.preprocess_mockable_headers(mocks_list, @preprocess_file_proc) @project_config_manager.process_test_defines_change(mocks_list)
@preprocessinator_helper.preprocess_mockable_headers(mocks_list, @preprocess_mock_file_proc)
@task_invoker.invoke_test_mocks(mocks_list) @task_invoker.invoke_test_mocks(mocks_list)
@preprocessinator_helper.preprocess_test_file(test, @preprocess_file_proc) if (@configurator.project_use_preprocessor_directives)
@preprocessinator_helper.preprocess_test_file(test, @preprocess_test_file_directives_proc)
else
@preprocessinator_helper.preprocess_test_file(test, @preprocess_test_file_proc)
end
return mocks_list return mocks_list
end end
@ -39,4 +48,9 @@ class Preprocessinator
@preprocessinator_file_handler.preprocess_file( filepath, @yaml_wrapper.load(@file_path_utils.form_preprocessed_includes_list_filepath(filepath)) ) @preprocessinator_file_handler.preprocess_file( filepath, @yaml_wrapper.load(@file_path_utils.form_preprocessed_includes_list_filepath(filepath)) )
end end
def preprocess_file_directives(filepath)
@preprocessinator_includes_handler.invoke_shallow_includes_list( filepath )
@preprocessinator_file_handler.preprocess_file_directives( filepath,
@yaml_wrapper.load( @file_path_utils.form_preprocessed_includes_list_filepath( filepath ) ) )
end
end end

View File

@ -16,6 +16,7 @@ class PreprocessinatorExtractor
lines = [] lines = []
File.readlines(filepath).each do |line| File.readlines(filepath).each do |line|
line.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
if found_file and not line =~ not_pragma if found_file and not line =~ not_pragma
lines << line lines << line
else else
@ -27,4 +28,28 @@ class PreprocessinatorExtractor
return lines return lines
end end
def extract_base_file_from_preprocessed_directives(filepath)
# preprocessing by way of toolchain preprocessor eliminates directives only
# like #ifdef's and leave other code
# iterate through all lines and only get last chunk of file after a last
# '#'line containing file name of our filepath
base_name = File.basename(filepath)
pattern = /^#.*(\s|\/|\\|\")#{Regexp.escape(base_name)}/
found_file = false # have we found the file we care about?
lines = []
File.readlines(filepath).each do |line|
line.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
lines << line
if line =~ pattern
lines = []
end
end
return lines
end
end end

View File

@ -18,4 +18,17 @@ class PreprocessinatorFileHandler
@file_wrapper.write(preprocessed_filepath, contents.join("\n")) @file_wrapper.write(preprocessed_filepath, contents.join("\n"))
end end
def preprocess_file_directives(filepath, includes)
preprocessed_filepath = @file_path_utils.form_preprocessed_file_filepath(filepath)
command = @tool_executor.build_command_line(@configurator.tools_test_file_preprocessor_directives, [], filepath, preprocessed_filepath)
@tool_executor.exec(command[:line], command[:options])
contents = @preprocessinator_extractor.extract_base_file_from_preprocessed_directives(preprocessed_filepath)
includes.each{|include| contents.unshift("#include \"#{include}\"")}
@file_wrapper.write(preprocessed_filepath, contents.join("\n"))
end
end end

View File

@ -15,6 +15,10 @@ class PreprocessinatorHelper
end end
end end
def preprocess_source_includes(test)
@test_includes_extractor.parse_test_file_source_include(test)
end
def assemble_mocks_list(test) def assemble_mocks_list(test)
return @file_path_utils.form_mocks_source_filelist( @test_includes_extractor.lookup_raw_mock_list(test) ) return @file_path_utils.form_mocks_source_filelist( @test_includes_extractor.lookup_raw_mock_list(test) )
end end

View File

@ -2,7 +2,7 @@
class PreprocessinatorIncludesHandler class PreprocessinatorIncludesHandler
constructor :configurator, :tool_executor, :task_invoker, :file_path_utils, :yaml_wrapper, :file_wrapper constructor :configurator, :tool_executor, :task_invoker, :file_path_utils, :yaml_wrapper, :file_wrapper, :file_finder
@@makefile_cache = {} @@makefile_cache = {}
# shallow includes: only those headers a source file explicitly includes # shallow includes: only those headers a source file explicitly includes
@ -65,6 +65,7 @@ class PreprocessinatorIncludesHandler
to_process = [filepath] to_process = [filepath]
ignore_list = [] ignore_list = []
list = [] list = []
all_mocks = []
include_paths = @configurator.project_config_hash[:collection_paths_include] include_paths = @configurator.project_config_hash[:collection_paths_include]
include_paths = [] if include_paths.nil? include_paths = [] if include_paths.nil?
@ -73,12 +74,10 @@ class PreprocessinatorIncludesHandler
while to_process.length > 0 while to_process.length > 0
target = to_process.shift() target = to_process.shift()
ignore_list << target ignore_list << target
# puts "[HELL] Processing: \t\t#{target}" new_deps, new_to_process, all_mocks = extract_includes_helper(target, include_paths, ignore_list, all_mocks)
new_deps, new_to_process = extract_includes_helper(target, include_paths, ignore_list)
list += new_deps list += new_deps
to_process += new_to_process to_process += new_to_process
if (!@configurator.project_config_hash.has_key?(:project_auto_link_deep_dependencies) or if !@configurator.project_config_hash[:project_auto_link_deep_dependencies]
!@configurator.project_config_hash[:project_auto_link_deep_dependencies])
break break
else else
list = list.uniq() list = list.uniq()
@ -89,93 +88,102 @@ class PreprocessinatorIncludesHandler
return list return list
end end
def extract_includes_helper(filepath, include_paths, ignore_list) def extract_includes_helper(filepath, include_paths, ignore_list, mocks)
# Extract the dependencies from the make rule # Extract the dependencies from the make rule
hdr_ext = @configurator.extension_header
make_rule = self.form_shallow_dependencies_rule(filepath) make_rule = self.form_shallow_dependencies_rule(filepath)
dependencies = make_rule.split.find_all {|path| path.end_with?(hdr_ext) }.uniq target_file = make_rule.split[0].gsub(':', '').gsub('\\','/')
dependencies.map! {|hdr| hdr.gsub('\\','/') } base = File.basename(target_file, File.extname(target_file))
make_rule_dependencies = make_rule.gsub(/.*\b#{Regexp.escape(base)}\S*/, '').gsub(/\\$/, '')
# Extract the headers dependencies from the make rule
hdr_ext = @configurator.extension_header
headers_dependencies = make_rule_dependencies.split.find_all {|path| path.end_with?(hdr_ext) }.uniq
headers_dependencies.map! {|hdr| hdr.gsub('\\','/') }
full_path_headers_dependencies = extract_full_path_dependencies(headers_dependencies)
# Separate the real files form the annotated ones and remove the '@@@@' # Extract the sources dependencies from the make rule
annotated_headers, real_headers = dependencies.partition {|hdr| hdr =~ /^@@@@/ } src_ext = @configurator.extension_source
annotated_headers.map! {|hdr| hdr.gsub('@@@@','') } sources_dependencies = make_rule_dependencies.split.find_all {|path| path.end_with?(src_ext) }.uniq
# Matching annotated_headers values against real_headers to ensure that sources_dependencies.map! {|src| src.gsub('\\','/') }
# annotated_headers contain full path entries (as returned by make rule) full_path_sources_dependencies = extract_full_path_dependencies(sources_dependencies)
annotated_headers.map! {|hdr| real_headers.find {|real_hdr| !real_hdr.match(/(.*\/)?#{Regexp.escape(hdr)}/).nil? } }
annotated_headers = annotated_headers.compact
# Find which of our annotated headers are "real" dependencies. This is list = full_path_headers_dependencies + full_path_sources_dependencies
# intended to weed out dependencies that have been removed due to build
# options defined in the project yaml and/or in the headers themselves. mock_prefix = @configurator.project_config_hash[:cmock_mock_prefix]
list = annotated_headers.find_all do |annotated_header| # Creating list of mocks
# find the index of the "real" include that matches the annotated one. mocks += full_path_headers_dependencies.find_all do |header|
idx = real_headers.find_index do |real_header| File.basename(header) =~ /^#{mock_prefix}.*$/
real_header =~ /^(.*\/)?#{Regexp.escape(annotated_header)}$/
end
# If we found a real include, delete it from the array and return it,
# otherwise return nil. Since nil is falsy this has the effect of making
# find_all return only the annotated headers for which a real include was
# found/deleted
idx ? real_headers.delete_at(idx) : nil
end.compact end.compact
# Extract direct dependencies that were also added # ignore real file when both mock and real file exist
src_ext = @configurator.extension_source mocks.each do |mock|
sdependencies = make_rule.split.find_all {|path| path.end_with?(src_ext) }.uniq list.each do |filename|
sdependencies.map! {|hdr| hdr.gsub('\\','/') } if File.basename(filename) == File.basename(mock).sub(mock_prefix, '')
list += sdependencies ignore_list << filename
end
end
end.compact
# Filtering list of final includes to only include mocks and anything that is NOT in the ignore_list
list = list.select do |item|
mocks.include? item or !(ignore_list.any? { |ignore_item| !item.match(/^(.*\/)?#{Regexp.escape(ignore_item)}$/).nil? })
end
to_process = [] to_process = []
if @configurator.project_config_hash.has_key?(:project_auto_link_deep_dependencies) && @configurator.project_config_hash[:project_auto_link_deep_dependencies] if @configurator.project_config_hash[:project_auto_link_deep_dependencies]
# Creating list of mocks
mocks = annotated_headers.find_all do |annotated_header|
File.basename(annotated_header) =~ /^#{@configurator.project_config_hash[:cmock_mock_prefix]}.*$/
end.compact
# Creating list of headers that should be recursively pre-processed # Creating list of headers that should be recursively pre-processed
# Skipping mocks and unity.h # Skipping mocks and vendor headers
headers_to_deep_link = annotated_headers.select do |annotated_header| headers_to_deep_link = full_path_headers_dependencies.select do |hdr|
!(mocks.include? annotated_header) and (annotated_header.match(/^(.*\/)?unity\.h$/).nil?) !(mocks.include? hdr) and (hdr.match(/^(.*\/)(#{VENDORS_FILES.join('|')}) + #{Regexp.escape(hdr_ext)}$/).nil?)
end
headers_to_deep_link.map! {|hdr| File.expand_path(hdr)}
mocks.each do |mock|
dirname = File.dirname(mock)
#basename = File.basename(mock).delete_prefix(@configurator.project_config_hash[:cmock_mock_prefix])
basename = File.basename(mock).sub(@configurator.project_config_hash[:cmock_mock_prefix], '')
if dirname != "."
ignore_list << File.join(dirname, basename)
else
ignore_list << basename
end
end.compact
# Filtering list of final includes to only include mocks and anything that is NOT in the ignore_list
list = list.select do |item|
mocks.include? item or !(ignore_list.any? { |ignore_item| !item.match(/^(.*\/)?#{Regexp.escape(ignore_item)}$/).nil? })
end end
headers_to_deep_link.map! {|hdr| File.expand_path(hdr) }
headers_to_deep_link.compact!
headers_to_deep_link.each do |hdr| headers_to_deep_link.each do |hdr|
if (ignore_list.none? {|ignore_header| hdr.match(/^(.*\/)?#{Regexp.escape(ignore_header)}$/)} and if (ignore_list.none? {|ignore_header| hdr.match(/^(.*\/)?#{Regexp.escape(ignore_header)}$/)} and
include_paths.none? {|include_path| hdr =~ /^#{include_path}\.*/}) include_paths.none? {|include_path| hdr =~ /^#{include_path}\.*/})
if File.exist?(hdr) if File.exist?(hdr)
to_process << hdr to_process << hdr
#source_file = hdr.delete_suffix(hdr_ext) + src_ext src = @file_finder.find_compilation_input_file(hdr, :ignore)
source_file = hdr.chomp(hdr_ext) + src_ext to_process << src if src
if source_file != hdr and File.exist?(source_file)
to_process << source_file
end
end end
end end
end end
end end
return list, to_process return list, to_process, mocks
end end
def write_shallow_includes_list(filepath, list) def write_shallow_includes_list(filepath, list)
@yaml_wrapper.dump(filepath, list) @yaml_wrapper.dump(filepath, list)
end end
private
def extract_full_path_dependencies(dependencies)
# Separate the real files form the annotated ones and remove the '@@@@'
annotated_files, real_files = dependencies.partition {|file| file =~ /^@@@@/}
annotated_files.map! {|file| file.gsub('@@@@','') }
# Matching annotated_files values against real_files to ensure that
# annotated_files contain full path entries (as returned by make rule)
annotated_files.map! {|file| real_files.find {|real| !real.match(/^(.*\/)?#{Regexp.escape(file)}$/).nil?}}
annotated_files = annotated_files.compact
# Find which of our annotated files are "real" dependencies. This is
# intended to weed out dependencies that have been removed due to build
# options defined in the project yaml and/or in the files themselves.
return annotated_files.find_all do |annotated_file|
# find the index of the "real" file that matches the annotated one.
idx = real_files.find_index do |real_file|
real_file =~ /^(.*\/)?#{Regexp.escape(annotated_file)}$/
end
# If we found a real file, delete it from the array and return it,
# otherwise return nil. Since nil is falsy this has the effect of making
# find_all return only the annotated filess for which a real file was
# found/deleted
idx ? real_files.delete_at(idx) : nil
end.compact
end
end end

View File

@ -21,9 +21,15 @@ class ProjectConfigManager
@options_files << File.basename( option_filepath ) @options_files << File.basename( option_filepath )
config_hash.deep_merge!( @yaml_wrapper.load( option_filepath ) ) config_hash.deep_merge!( @yaml_wrapper.load( option_filepath ) )
end end
def filter_internal_sources(sources)
filtered_sources = sources.clone
filtered_sources.delete_if { |item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{Regexp.escape(EXTENSION_SOURCE)}$/ }
filtered_sources.delete_if { |item| item =~ /#{VENDORS_FILES.map{|source| '\b' + Regexp.escape(source.ext(EXTENSION_SOURCE)) + '\b'}.join('|')}$/ }
return filtered_sources
end
def process_release_config_change def process_release_config_change
# has project configuration changed since last release build # has project configuration changed since last release build
@release_config_changed = @cacheinator.diff_cached_release_config?( @config_hash ) @release_config_changed = @cacheinator.diff_cached_release_config?( @config_hash )
@ -40,7 +46,7 @@ class ProjectConfigManager
@test_defines_changed = @cacheinator.diff_cached_test_defines?( files ) @test_defines_changed = @cacheinator.diff_cached_test_defines?( files )
if @test_defines_changed if @test_defines_changed
# update timestamp for rake task prerequisites # update timestamp for rake task prerequisites
@file_wrapper.touch( @configurator.project_test_force_rebuild_filepath ) @file_wrapper.touch( @configurator.project_test_force_rebuild_filepath, :mtime => Time.now + 10 )
end end
end end
end end

View File

@ -10,7 +10,6 @@ $LOAD_PATH.unshift( CEEDLING_LIB )
$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'unity/auto') ) $LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'unity/auto') )
$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'diy/lib') ) $LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'diy/lib') )
$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'cmock/lib') ) $LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'cmock/lib') )
$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'deep_merge/lib') )
require 'rake' require 'rake'

View File

@ -56,15 +56,40 @@ class ReleaseInvoker
end end
def convert_libraries_to_arguments(libraries) def convert_libraries_to_arguments(libraries)
args = (libraries || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : []) args = ((libraries || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : [])).flatten
if (defined? LIBRARIES_FLAG) if (defined? LIBRARIES_FLAG)
args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) } args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) }
end end
return args return args
end end
def get_library_paths_to_arguments()
paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : []
if (defined? LIBRARIES_PATH_FLAG)
paths.map! {|v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) }
end
return paths
end
def sort_objects_and_libraries(both) def sort_objects_and_libraries(both)
extension = "\\" + ((defined? EXTENSION_SUBPROJECTS) ? EXTENSION_SUBPROJECTS : ".LIBRARY") extension = if ((defined? EXTENSION_SUBPROJECTS) && (defined? EXTENSION_LIBRARIES))
extension_libraries = if (EXTENSION_LIBRARIES.class == Array)
EXTENSION_LIBRARIES.join(")|(?:\\")
else
EXTENSION_LIBRARIES
end
"(?:\\#{EXTENSION_SUBPROJECTS})|(?:\\#{extension_libraries})"
elsif (defined? EXTENSION_SUBPROJECTS)
"\\#{EXTENSION_SUBPROJECTS}"
elsif (defined? EXTENSION_LIBRARIES)
if (EXTENSION_LIBRARIES.class == Array)
"(?:\\#{EXTENSION_LIBRARIES.join(")|(?:\\")})"
else
"\\#{EXTENSION_LIBRARIES}"
end
else
"\\.LIBRARY"
end
sorted_objects = both.group_by {|v| v.match(/.+#{extension}$/) ? :libraries : :objects } sorted_objects = both.group_by {|v| v.match(/.+#{extension}$/) ? :libraries : :objects }
libraries = sorted_objects[:libraries] || [] libraries = sorted_objects[:libraries] || []
objects = sorted_objects[:objects] || [] objects = sorted_objects[:objects] || []

View File

@ -2,6 +2,17 @@
RELEASE_COMPILE_TASK_ROOT = RELEASE_TASK_ROOT + 'compile:' unless defined?(RELEASE_COMPILE_TASK_ROOT) RELEASE_COMPILE_TASK_ROOT = RELEASE_TASK_ROOT + 'compile:' unless defined?(RELEASE_COMPILE_TASK_ROOT)
RELEASE_ASSEMBLE_TASK_ROOT = RELEASE_TASK_ROOT + 'assemble:' unless defined?(RELEASE_ASSEMBLE_TASK_ROOT) RELEASE_ASSEMBLE_TASK_ROOT = RELEASE_TASK_ROOT + 'assemble:' unless defined?(RELEASE_ASSEMBLE_TASK_ROOT)
# If GCC and Releasing a Library, Update Tools to Automatically Have Necessary Tags
if (TOOLS_RELEASE_COMPILER[:executable] == DEFAULT_RELEASE_COMPILER_TOOL[:executable])
if (File.extname(PROJECT_RELEASE_BUILD_TARGET) == '.so')
TOOLS_RELEASE_COMPILER[:arguments] << "-fPIC" unless TOOLS_RELEASE_COMPILER[:arguments].include?("-fPIC")
TOOLS_RELEASE_LINKER[:arguments] << "-shared" unless TOOLS_RELEASE_LINKER[:arguments].include?("-shared")
elsif (File.extname(PROJECT_RELEASE_BUILD_TARGET) == '.a')
TOOLS_RELEASE_COMPILER[:arguments] << "-fPIC" unless TOOLS_RELEASE_COMPILER[:arguments].include?("-fPIC")
TOOLS_RELEASE_LINKER[:executable] = 'ar'
TOOLS_RELEASE_LINKER[:arguments] = ['rcs', '${2}', '${1}'].compact
end
end
if (RELEASE_BUILD_USE_ASSEMBLY) if (RELEASE_BUILD_USE_ASSEMBLY)
rule(/#{PROJECT_RELEASE_BUILD_OUTPUT_ASM_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [ rule(/#{PROJECT_RELEASE_BUILD_OUTPUT_ASM_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [
@ -37,16 +48,18 @@ end
rule(/#{PROJECT_RELEASE_BUILD_TARGET}/) do |bin_file| rule(/#{PROJECT_RELEASE_BUILD_TARGET}/) do |bin_file|
objects, libraries = @ceedling[:release_invoker].sort_objects_and_libraries(bin_file.prerequisites) objects, libraries = @ceedling[:release_invoker].sort_objects_and_libraries(bin_file.prerequisites)
tool = TOOLS_RELEASE_LINKER.clone tool = TOOLS_RELEASE_LINKER.clone
lib_args = @ceedling[:release_invoker].convert_libraries_to_arguments(libraries) lib_args = @ceedling[:release_invoker].convert_libraries_to_arguments(libraries)
map_file = @ceedling[:configurator].project_release_build_map lib_paths = @ceedling[:release_invoker].get_library_paths_to_arguments()
map_file = @ceedling[:configurator].project_release_build_map
@ceedling[:generator].generate_executable_file( @ceedling[:generator].generate_executable_file(
tool, tool,
RELEASE_SYM, RELEASE_SYM,
objects, objects,
bin_file.name, bin_file.name,
map_file, map_file,
lib_args ) lib_args,
lib_paths )
@ceedling[:release_invoker].artifactinate( bin_file.name, map_file, @ceedling[:configurator].release_build_artifacts ) @ceedling[:release_invoker].artifactinate( bin_file.name, map_file, @ceedling[:configurator].release_build_artifacts )
end end

View File

@ -34,16 +34,16 @@ end
rule(/#{PROJECT_TEST_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file| rule(/#{PROJECT_TEST_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file|
lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments()
lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments()
@ceedling[:generator].generate_executable_file( @ceedling[:generator].generate_executable_file(
TOOLS_TEST_LINKER, TOOLS_TEST_LINKER,
TEST_SYM, TEST_SYM,
bin_file.prerequisites, bin_file.prerequisites,
bin_file.name, bin_file.name,
@ceedling[:file_path_utils].form_test_build_map_filepath( bin_file.name ), @ceedling[:file_path_utils].form_test_build_map_filepath( bin_file.name ),
lib_args ) lib_args,
lib_paths )
end end
@ -66,8 +66,7 @@ namespace TEST_SYM do
@ceedling[:file_finder].find_test_from_file_path(test) @ceedling[:file_finder].find_test_from_file_path(test)
end end
]) do |test| ]) do |test|
@ceedling[:rake_wrapper][:directories].reenable if @ceedling[:task_invoker].first_run == false && @ceedling[:project_config_manager].test_defines_changed @ceedling[:rake_wrapper][:test_deps].invoke
@ceedling[:rake_wrapper][:directories].invoke
@ceedling[:test_invoker].setup_and_invoke([test.source]) @ceedling[:test_invoker].setup_and_invoke([test.source])
end end
end end

View File

@ -25,8 +25,8 @@ class Setupinator
@ceedling[:configurator].populate_cmock_defaults( config_hash ) @ceedling[:configurator].populate_cmock_defaults( config_hash )
@ceedling[:configurator].find_and_merge_plugins( config_hash ) @ceedling[:configurator].find_and_merge_plugins( config_hash )
@ceedling[:configurator].merge_imports( config_hash ) @ceedling[:configurator].merge_imports( config_hash )
@ceedling[:configurator].tools_setup( config_hash )
@ceedling[:configurator].eval_environment_variables( config_hash ) @ceedling[:configurator].eval_environment_variables( config_hash )
@ceedling[:configurator].tools_setup( config_hash )
@ceedling[:configurator].eval_paths( config_hash ) @ceedling[:configurator].eval_paths( config_hash )
@ceedling[:configurator].standardize_paths( config_hash ) @ceedling[:configurator].standardize_paths( config_hash )
@ceedling[:configurator].validate( config_hash ) @ceedling[:configurator].validate( config_hash )

View File

@ -46,25 +46,31 @@ class TaskInvoker
return @rake_utils.task_invoked?(regex) return @rake_utils.task_invoked?(regex)
end end
def reset_rake_task_for_changed_defines(file)
if !(file =~ /#{VENDORS_FILES.map{|ignore| '\b' + ignore.ext(File.extname(file)) + '\b'}.join('|')}$/)
@rake_wrapper[file].clear_actions if @first_run == false && @project_config_manager.test_defines_changed
@rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed
end
end
def invoke_test_mocks(mocks) def invoke_test_mocks(mocks)
@dependinator.enhance_mock_dependencies( mocks ) @dependinator.enhance_mock_dependencies( mocks )
mocks.each { |mock| mocks.each { |mock|
@rake_wrapper[mock].reenable if @first_run == false && @project_config_manager.test_defines_changed reset_rake_task_for_changed_defines( mock )
@rake_wrapper[mock].invoke @rake_wrapper[mock].invoke
} }
end end
def invoke_test_runner(runner) def invoke_test_runner(runner)
@dependinator.enhance_runner_dependencies( runner ) @dependinator.enhance_runner_dependencies( runner )
@rake_wrapper[runner].reenable if @first_run == false && @project_config_manager.test_defines_changed reset_rake_task_for_changed_defines( runner )
@rake_wrapper[runner].invoke @rake_wrapper[runner].invoke
end end
def invoke_test_shallow_include_lists(files) def invoke_test_shallow_include_lists(files)
@dependinator.enhance_shallow_include_lists_dependencies( files ) @dependinator.enhance_shallow_include_lists_dependencies( files )
par_map(PROJECT_COMPILE_THREADS, files) do |file| par_map(PROJECT_COMPILE_THREADS, files) do |file|
@rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed reset_rake_task_for_changed_defines( file )
@rake_wrapper[file].invoke @rake_wrapper[file].invoke
end end
end end
@ -72,7 +78,7 @@ class TaskInvoker
def invoke_test_preprocessed_files(files) def invoke_test_preprocessed_files(files)
@dependinator.enhance_preprocesed_file_dependencies( files ) @dependinator.enhance_preprocesed_file_dependencies( files )
par_map(PROJECT_COMPILE_THREADS, files) do |file| par_map(PROJECT_COMPILE_THREADS, files) do |file|
@rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed reset_rake_task_for_changed_defines( file )
@rake_wrapper[file].invoke @rake_wrapper[file].invoke
end end
end end
@ -80,14 +86,14 @@ class TaskInvoker
def invoke_test_dependencies_files(files) def invoke_test_dependencies_files(files)
@dependinator.enhance_dependencies_dependencies( files ) @dependinator.enhance_dependencies_dependencies( files )
par_map(PROJECT_COMPILE_THREADS, files) do |file| par_map(PROJECT_COMPILE_THREADS, files) do |file|
@rake_wrapper[file].reenable if @first_run == false && @project_config_manager.test_defines_changed reset_rake_task_for_changed_defines( file )
@rake_wrapper[file].invoke @rake_wrapper[file].invoke
end end
end end
def invoke_test_objects(objects) def invoke_test_objects(objects)
par_map(PROJECT_COMPILE_THREADS, objects) do |object| par_map(PROJECT_COMPILE_THREADS, objects) do |object|
@rake_wrapper[object].reenable if @first_run == false && @project_config_manager.test_defines_changed reset_rake_task_for_changed_defines( object )
@rake_wrapper[object].invoke @rake_wrapper[object].invoke
end end
end end
@ -98,7 +104,6 @@ class TaskInvoker
def invoke_test_results(result) def invoke_test_results(result)
@dependinator.enhance_results_dependencies( result ) @dependinator.enhance_results_dependencies( result )
@rake_wrapper[result].reenable if @first_run == false && @project_config_manager.test_defines_changed
@rake_wrapper[result].invoke @rake_wrapper[result].invoke
end end

View File

@ -4,28 +4,10 @@ require 'ceedling/version'
desc "Display build environment version info." desc "Display build environment version info."
task :version do task :version do
puts " Ceedling:: #{Ceedling::Version::CEEDLING}" puts " Ceedling:: #{Ceedling::Version::CEEDLING}"
puts " Unity:: #{Ceedling::Version::UNITY}"
[ puts " CMock:: #{Ceedling::Version::CMOCK}"
['CException', File.join( CEEDLING_VENDOR, CEXCEPTION_ROOT_PATH)], puts " CException:: #{Ceedling::Version::CEXCEPTION}"
[' CMock', File.join( CEEDLING_VENDOR, CMOCK_ROOT_PATH)],
[' Unity', File.join( CEEDLING_VENDOR, UNITY_ROOT_PATH)],
].each do |tool|
name = tool[0]
base_path = tool[1]
version_string = begin
@ceedling[:file_wrapper].read( File.join(base_path, 'release', 'version.info') ).strip
rescue
"UNKNOWN"
end
build_string = begin
@ceedling[:file_wrapper].read( File.join(base_path, 'release', 'build.info') ).strip
rescue
"UNKNOWN"
end
puts "#{name}:: #{version_string.empty? ? '#.#.' : (version_string + '.')}#{build_string.empty? ? '?' : build_string}"
end
end end
desc "Set verbose output (silent:[#{Verbosity::SILENT}] - obnoxious:[#{Verbosity::OBNOXIOUS}])." desc "Set verbose output (silent:[#{Verbosity::SILENT}] - obnoxious:[#{Verbosity::OBNOXIOUS}])."
@ -65,6 +47,12 @@ task :sanity_checks, :level do |t, args|
@ceedling[:configurator].sanity_checks = check_level @ceedling[:configurator].sanity_checks = check_level
end end
# non advertised catch for calling upgrade in the wrong place
task :upgrade do
puts "WARNING: You're currently IN your project directory. Take a step out and try"
puts "again if you'd like to perform an upgrade."
end
# list expanded environment variables # list expanded environment variables
if (not ENVIRONMENT.empty?) if (not ENVIRONMENT.empty?)
desc "List all configured environment variables." desc "List all configured environment variables."
@ -73,7 +61,7 @@ task :environment do
ENVIRONMENT.each do |env| ENVIRONMENT.each do |env|
env.each_key do |key| env.each_key do |key|
name = key.to_s.upcase name = key.to_s.upcase
env_list.push(" - #{name}: \"#{env[key]}\"") env_list.push(" - #{name}: \"#{env[key]}\"")
end end
end end
env_list.sort.each do |env_line| env_list.sort.each do |env_line|
@ -88,7 +76,7 @@ namespace :options do
option = File.basename(option_path, '.yml') option = File.basename(option_path, '.yml')
desc "Merge #{option} project options." desc "Merge #{option} project options."
task option.downcase.to_sym do task option.to_sym do
hash = @ceedling[:project_config_manager].merge_options( @ceedling[:setupinator].config_hash, option_path ) hash = @ceedling[:project_config_manager].merge_options( @ceedling[:setupinator].config_hash, option_path )
@ceedling[:setupinator].do_setup( hash ) @ceedling[:setupinator].do_setup( hash )
if @ceedling[:configurator].project_release_build if @ceedling[:configurator].project_release_build
@ -97,6 +85,23 @@ namespace :options do
end end
end end
# This is to give nice errors when typing options
rule /^options:.*/ do |t, args|
filename = t.to_s.split(':')[-1] + '.yml'
filelist = COLLECTION_PROJECT_OPTIONS.map{|s| File.basename(s) }
@ceedling[:file_finder].find_file_from_list(filename, filelist, :error)
end
# This will output the fully-merged tools options to their own project.yml file
desc "Export tools options to a new project file"
task :export, :filename do |t, args|
outfile = args.filename || 'tools.yml'
toolcfg = {}
@ceedling[:configurator].project_config_hash.each_pair do |k,v|
toolcfg[k] = v if (k.to_s[0..5] == 'tools_')
end
File.open(outfile,'w') {|f| f << toolcfg.to_yaml({:indentation => 2})}
end
end end

View File

@ -45,26 +45,35 @@ task(:clobber => [:clean]) do
@ceedling[:streaminator].stdout_puts("\nClobbering all generated files...\n(For large projects, this task may take a long time to complete)\n\n") @ceedling[:streaminator].stdout_puts("\nClobbering all generated files...\n(For large projects, this task may take a long time to complete)\n\n")
begin begin
CLOBBER.each { |fn| REMOVE_FILE_PROC.call(fn) } CLOBBER.each { |fn| REMOVE_FILE_PROC.call(fn) }
@ceedling[:rake_wrapper][:directories].invoke
@ceedling[:dependinator].touch_force_rebuild_files
rescue rescue
end end
end end
# create a directory task for each of the paths, so we know how to build them
PROJECT_BUILD_PATHS.each { |path| directory(path) } PROJECT_BUILD_PATHS.each { |path| directory(path) }
# create directories that hold build output and generated files & touching rebuild dependency sources # create a single directory task which verifies all the others get built
task(:directories => PROJECT_BUILD_PATHS) { @ceedling[:dependinator].touch_force_rebuild_files } task :directories => PROJECT_BUILD_PATHS
# when the force file doesn't exist, it probably means we clobbered or are on a fresh
# install. In either case, stuff was deleted, so assume we want to rebuild it all
file @ceedling[:configurator].project_test_force_rebuild_filepath do
unless File.exists?(@ceedling[:configurator].project_test_force_rebuild_filepath)
@ceedling[:dependinator].touch_force_rebuild_files
end
end
# list paths discovered at load time # list paths discovered at load time
namespace :paths do namespace :paths do
standard_paths = ['test','source','include']
paths = @ceedling[:setupinator].config_hash[:paths] paths = @ceedling[:setupinator].config_hash[:paths].keys.map{|n| n.to_s.downcase}
paths.each_key do |section| paths = (paths + standard_paths).uniq
name = section.to_s.downcase paths.each do |name|
path_list = Object.const_get("COLLECTION_PATHS_#{name.upcase}") path_list = Object.const_get("COLLECTION_PATHS_#{name.upcase}")
if (path_list.size != 0) if (path_list.size != 0) || (standard_paths.include?(name))
desc "List all collected #{name} paths." desc "List all collected #{name} paths."
task(name.to_sym) { puts "#{name} paths:"; path_list.sort.each {|path| puts " - #{path}" } } task(name.to_sym) { puts "#{name} paths:"; path_list.sort.each {|path| puts " - #{path}" } }
end end
@ -77,10 +86,11 @@ end
namespace :files do namespace :files do
categories = [ categories = [
['test', COLLECTION_ALL_TESTS], ['test', COLLECTION_ALL_TESTS],
['source', COLLECTION_ALL_SOURCE], ['source', COLLECTION_ALL_SOURCE],
['header', COLLECTION_ALL_HEADERS] ['include', COLLECTION_ALL_HEADERS],
] ['support', COLLECTION_ALL_SUPPORT]
]
using_assembly = (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) || using_assembly = (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) ||
(defined?(RELEASE_BUILD_USE_ASSEMBLY) && RELEASE_BUILD_USE_ASSEMBLY) (defined?(RELEASE_BUILD_USE_ASSEMBLY) && RELEASE_BUILD_USE_ASSEMBLY)

View File

@ -1,13 +1,15 @@
require 'ceedling/constants' require 'ceedling/constants'
task :test => [:directories] do task :test_deps => [:directories]
task :test => [:test_deps] do
Rake.application['test:all'].invoke Rake.application['test:all'].invoke
end end
namespace TEST_SYM do namespace TEST_SYM do
desc "Run all unit tests (also just 'test' works)." desc "Run all unit tests (also just 'test' works)."
task :all => [:directories] do task :all => [:test_deps] do
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS)
end end
@ -21,17 +23,17 @@ namespace TEST_SYM do
end end
desc "Run tests for changed files." desc "Run tests for changed files."
task :delta => [:directories] do task :delta => [:test_deps] do
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:force_run => false}) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:force_run => false})
end end
desc "Just build tests without running." desc "Just build tests without running."
task :build_only => [:directories] do task :build_only => [:test_deps] do
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:build_only => true}) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:build_only => true})
end end
desc "Run tests by matching regular expression pattern." desc "Run tests by matching regular expression pattern."
task :pattern, [:regex] => [:directories] do |t, args| task :pattern, [:regex] => [:test_deps] do |t, args|
matches = [] matches = []
COLLECTION_ALL_TESTS.each { |test| matches << test if (test =~ /#{args.regex}/) } COLLECTION_ALL_TESTS.each { |test| matches << test if (test =~ /#{args.regex}/) }
@ -44,7 +46,7 @@ namespace TEST_SYM do
end end
desc "Run tests whose test path contains [dir] or [dir] substring." desc "Run tests whose test path contains [dir] or [dir] substring."
task :path, [:dir] => [:directories] do |t, args| task :path, [:dir] => [:test_deps] do |t, args|
matches = [] matches = []
COLLECTION_ALL_TESTS.each { |test| matches << test if File.dirname(test).include?(args.dir.gsub(/\\/, '/')) } COLLECTION_ALL_TESTS.each { |test| matches << test if File.dirname(test).include?(args.dir.gsub(/\\/, '/')) }

View File

@ -19,6 +19,11 @@ class TestIncludesExtractor
gather_and_store_includes( test, extract_from_file(test) ) gather_and_store_includes( test, extract_from_file(test) )
end end
# open, scan for, and sort & store includes of test file
def parse_test_file_source_include(test)
return extract_source_include_from_file(test)
end
# mocks with no file extension # mocks with no file extension
def lookup_raw_mock_list(test) def lookup_raw_mock_list(test)
file_key = form_file_key(test) file_key = form_file_key(test)
@ -65,6 +70,27 @@ class TestIncludesExtractor
return includes.uniq return includes.uniq
end end
def extract_source_include_from_file(file)
source_includes = []
source_extension = @configurator.extension_source
contents = @file_wrapper.read(file)
# remove line comments
contents = contents.gsub(/\/\/.*$/, '')
# remove block comments
contents = contents.gsub(/\/\*.*?\*\//m, '')
contents.split("\n").each do |line|
# look for include statement
scan_results = line.scan(/#include\s+\"\s*(.+#{'\\'+source_extension})\s*\"/)
source_includes << scan_results[0][0] if (scan_results.size > 0)
end
return source_includes.uniq
end
def gather_and_store_includes(file, includes) def gather_and_store_includes(file, includes)
mock_prefix = @configurator.cmock_mock_prefix mock_prefix = @configurator.cmock_mock_prefix
header_extension = @configurator.extension_header header_extension = @configurator.extension_header

View File

@ -23,49 +23,24 @@ class TestInvoker
@mocks = [] @mocks = []
end end
def get_test_definition_str(test)
return "-D" + File.basename(test, File.extname(test)).upcase.sub(/@.*$/, "")
end
def get_tools_compilers
tools_compilers = Hash.new
tools_compilers["for unit test"] = TOOLS_TEST_COMPILER if defined? TOOLS_TEST_COMPILER
tools_compilers["for gcov"] = TOOLS_GCOV_COMPILER if defined? TOOLS_GCOV_COMPILER
return tools_compilers
end
def add_test_definition(test)
test_definition_str = get_test_definition_str(test)
get_tools_compilers.each do |tools_compiler_key, tools_compiler_value|
tools_compiler_value[:arguments].push("-D#{File.basename(test, ".*").strip.upcase.sub(/@.*$/, "")}")
@streaminator.stdout_puts("Add the definition value in the build option #{tools_compiler_value[:arguments][-1]} #{tools_compiler_key}", Verbosity::OBNOXIOUS)
end
end
def delete_test_definition(test)
test_definition_str = get_test_definition_str(test)
get_tools_compilers.each do |tools_compiler_key, tools_compiler_value|
num_options = tools_compiler_value[:arguments].size
@streaminator.stdout_puts("Delete the definition value in the build option #{tools_compiler_value[:arguments][-1]} #{tools_compiler_key}", Verbosity::OBNOXIOUS)
tools_compiler_value[:arguments].delete_if{|i| i == test_definition_str}
if num_options > tools_compiler_value[:arguments].size + 1
@streaminator.stderr_puts("WARNING: duplicated test definition.")
end
end
end
# Convert libraries configuration form YAML configuration # Convert libraries configuration form YAML configuration
# into a string that can be given to the compiler. # into a string that can be given to the compiler.
def convert_libraries_to_arguments() def convert_libraries_to_arguments()
if @configurator.project_config_hash.has_key?(:libraries_test) args = ((@configurator.project_config_hash[:libraries_test] || []) + ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : [])).flatten
lib_args = @configurator.project_config_hash[:libraries_test] if (defined? LIBRARIES_FLAG)
lib_args.flatten! args.map! {|v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) }
lib_flag = @configurator.project_config_hash[:libraries_flag]
lib_args.map! {|v| lib_flag.gsub(/\$\{1\}/, v) } if (defined? lib_flag)
return lib_args
end end
return args
end end
def get_library_paths_to_arguments()
paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : []
if (defined? LIBRARIES_PATH_FLAG)
paths.map! {|v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) }
end
return paths
end
def setup_and_invoke(tests, context=TEST_SYM, options={:force_run => true, :build_only => false}) def setup_and_invoke(tests, context=TEST_SYM, options={:force_run => true, :build_only => false})
@ -83,18 +58,25 @@ class TestInvoker
test_name ="#{File.basename(test)}".chomp('.c') test_name ="#{File.basename(test)}".chomp('.c')
def_test_key="defines_#{test_name.downcase}" def_test_key="defines_#{test_name.downcase}"
# Re-define the project out path and pre-processor defines. if @configurator.project_config_hash.has_key?(def_test_key.to_sym) || @configurator.defines_use_test_definition
if @configurator.project_config_hash.has_key?(def_test_key.to_sym)
@project_config_manager.test_config_changed
defs_bkp = Array.new(COLLECTION_DEFINES_TEST_AND_VENDOR) defs_bkp = Array.new(COLLECTION_DEFINES_TEST_AND_VENDOR)
printf " ************** Specific test definitions for #{test_name} !!! \n" tst_defs_cfg = Array.new(defs_bkp)
tst_defs_cfg = @configurator.project_config_hash[def_test_key.to_sym] if @configurator.project_config_hash.has_key?(def_test_key.to_sym)
tst_defs_cfg.replace(@configurator.project_config_hash[def_test_key.to_sym])
tst_defs_cfg .concat(COLLECTION_DEFINES_VENDOR) if COLLECTION_DEFINES_VENDOR
end
if @configurator.defines_use_test_definition
tst_defs_cfg << File.basename(test, ".*").strip.upcase.sub(/@.*$/, "")
end
COLLECTION_DEFINES_TEST_AND_VENDOR.replace(tst_defs_cfg)
end
# redefine the project out path and preprocessor defines
if @configurator.project_config_hash.has_key?(def_test_key.to_sym)
@streaminator.stdout_puts("Updating test definitions for #{test_name}", Verbosity::NORMAL)
orig_path = @configurator.project_test_build_output_path orig_path = @configurator.project_test_build_output_path
@configurator.project_config_hash[:project_test_build_output_path] = File.join(@configurator.project_test_build_output_path, test_name) @configurator.project_config_hash[:project_test_build_output_path] = File.join(@configurator.project_test_build_output_path, test_name)
@file_wrapper.mkdir(@configurator.project_test_build_output_path) @file_wrapper.mkdir(@configurator.project_test_build_output_path)
COLLECTION_DEFINES_TEST_AND_VENDOR.replace(tst_defs_cfg)
# printf " * new defines = #{COLLECTION_DEFINES_TEST_AND_VENDOR}\n"
end end
# collect up test fixture pieces & parts # collect up test fixture pieces & parts
@ -103,16 +85,15 @@ class TestInvoker
sources = @test_invoker_helper.extract_sources( test ) sources = @test_invoker_helper.extract_sources( test )
extras = @configurator.collection_test_fixture_extra_link_objects extras = @configurator.collection_test_fixture_extra_link_objects
core = [test] + mock_list + sources core = [test] + mock_list + sources
objects = @file_path_utils.form_test_build_objects_filelist( [runner] + core + extras ) objects = @file_path_utils.form_test_build_objects_filelist( [runner] + core + extras ).uniq
results_pass = @file_path_utils.form_pass_results_filepath( test ) results_pass = @file_path_utils.form_pass_results_filepath( test )
results_fail = @file_path_utils.form_fail_results_filepath( test ) results_fail = @file_path_utils.form_fail_results_filepath( test )
@project_config_manager.process_test_defines_change(sources) # identify all the objects shall not be linked and then remove them from objects list.
no_link_objects = @file_path_utils.form_test_build_objects_filelist(@preprocessinator.preprocess_shallow_source_includes( test ))
objects = objects.uniq - no_link_objects
# add the definition value in the build option for the unit test @project_config_manager.process_test_defines_change(@project_config_manager.filter_internal_sources(sources))
if @configurator.defines_use_test_definition
add_test_definition(test)
end
# clean results files so we have a missing file with which to kick off rake's dependency rules # clean results files so we have a missing file with which to kick off rake's dependency rules
@test_invoker_helper.clean_results( {:pass => results_pass, :fail => results_fail}, options ) @test_invoker_helper.clean_results( {:pass => results_pass, :fail => results_fail}, options )
@ -129,7 +110,7 @@ class TestInvoker
@dependinator.enhance_test_build_object_dependencies( objects ) @dependinator.enhance_test_build_object_dependencies( objects )
# associate object files with executable # associate object files with executable
@dependinator.setup_test_executable_dependencies( test, objects ) @dependinator.enhance_test_executable_dependencies( test, objects )
# build test objects # build test objects
@task_invoker.invoke_test_objects( objects ) @task_invoker.invoke_test_objects( objects )
@ -146,18 +127,14 @@ class TestInvoker
rescue => e rescue => e
@build_invoker_utils.process_exception( e, context ) @build_invoker_utils.process_exception( e, context )
ensure ensure
# delete the definition value in the build option for the unit test
if @configurator.defines_use_test_definition
delete_test_definition(test)
end
@plugin_manager.post_test( test ) @plugin_manager.post_test( test )
# restore the project test defines # restore the project test defines
if @configurator.project_config_hash.has_key?(def_test_key.to_sym) if @configurator.project_config_hash.has_key?(def_test_key.to_sym) || @configurator.defines_use_test_definition
# @configurator.project_config_hash[:defines_test] =
COLLECTION_DEFINES_TEST_AND_VENDOR.replace(defs_bkp) COLLECTION_DEFINES_TEST_AND_VENDOR.replace(defs_bkp)
# printf " ---- Restored defines at #{defs_bkp}" if @configurator.project_config_hash.has_key?(def_test_key.to_sym)
@configurator.project_config_hash[:project_test_build_output_path] = orig_path @configurator.project_config_hash[:project_test_build_output_path] = orig_path
printf " ************** Restored defines and build path\n" @streaminator.stdout_puts("Restored defines and build path to standard", Verbosity::NORMAL)
end
end end
end end

View File

@ -11,7 +11,7 @@ class TestInvokerHelper
def process_deep_dependencies(files) def process_deep_dependencies(files)
return if (not @configurator.project_use_deep_dependencies) return if (not @configurator.project_use_deep_dependencies)
dependencies_list = @file_path_utils.form_test_dependencies_filelist( files ) dependencies_list = @file_path_utils.form_test_dependencies_filelist( files ).uniq
if @configurator.project_generate_deep_dependencies if @configurator.project_generate_deep_dependencies
@task_invoker.invoke_test_dependencies_files( dependencies_list ) @task_invoker.invoke_test_dependencies_files( dependencies_list )

View File

@ -2,35 +2,53 @@
# @private # @private
module Ceedling module Ceedling
module Version module Version
# Check for local or global version of vendor directory in order to look up versions { "UNITY" => File.join("unity","src","unity.h"),
{ "CMOCK" => File.join("cmock","src","cmock.h"),
"CEXCEPTION" => File.join("vendor","c_exception","lib","CException.h"), "CEXCEPTION" => File.join("c_exception","lib","CException.h")
"CMOCK" => File.join("vendor","cmock","src","cmock.h"),
"UNITY" => File.join("vendor","unity","src","unity.h"),
}.each_pair do |name, path| }.each_pair do |name, path|
filename = if (File.exist?(File.join("..","..",path))) # Check for local or global version of vendor directory in order to look up versions
File.join("..","..",path) path1 = File.expand_path( File.join("..","..","vendor",path) )
elsif (File.exist?(File.join(File.dirname(__FILE__),"..","..",path))) path2 = File.expand_path( File.join(File.dirname(__FILE__),"..","..","vendor",path) )
File.join(File.dirname(__FILE__),"..","..",path) filename = if (File.exists?(path1))
path1
elsif (File.exists?(path2))
path2
elsif File.exists?(CEEDLING_VENDOR)
path3 = File.expand_path( File.join(CEEDLING_VENDOR,path) )
if (File.exists?(path3))
path3
else
basepath = File.join( CEEDLING_VENDOR, path.split(/\\\//)[0], 'release')
begin
[ @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'version.info') ).strip,
@ceedling[:file_wrapper].read( File.join(base_path, 'release', 'build.info') ).strip ].join('.')
rescue
"#{name}"
end
end
else else
eval "#{name} = 'unknown'" module_eval("#{name} = 'unknown'")
continue continue
end end
# Actually look up the versions # Actually look up the versions
a = [0,0,0] a = [0,0,0]
File.readlines(filename) do |line| begin
["VERSION_MAJOR", "VERSION_MINOR", "VERSION_BUILD"].each_with_index do |field, i| File.readlines(filename).each do |line|
m = line.match(/#{name}_#{field}\s+(\d+)/) ["VERSION_MAJOR", "VERSION_MINOR", "VERSION_BUILD"].each_with_index do |field, i|
a[i] = m[1] unless (m.nil?) m = line.match(/#{name}_#{field}\s+(\d+)/)
a[i] = m[1] unless (m.nil?)
end
end end
rescue
abort("Can't collect data for vendor component: \"#{filename}\" . \nPlease check your setup.")
end end
# Make a constant from each, so that we can use it elsewhere # splat it to return the final value
eval "#{name} = '#{a.join(".")}'" eval("#{name} = '#{a.join(".")}'")
end end
GEM = "0.29.0" GEM = "0.31.1"
CEEDLING = GEM CEEDLING = GEM
end end
end end

View File

@ -1,15 +0,0 @@
# @private
module Ceedling
module Version
# @private
GEM = "0.27.0"
# @private
CEEDLING = "<%= versions["CEEDLING"] %>"
# @private
CEXCEPTION = "<%= versions["CEXCEPTION"] %>"
# @private
CMOCK = "<%= versions["CMOCK"] %>"
# @private
UNITY = "<%= versions["UNITY"] %>"
end
end

View File

@ -32,12 +32,16 @@ rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [
end end
rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file| rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file|
lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments()
lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments()
@ceedling[:generator].generate_executable_file( @ceedling[:generator].generate_executable_file(
TOOLS_BULLSEYE_LINKER, TOOLS_BULLSEYE_LINKER,
BULLSEYE_SYM, BULLSEYE_SYM,
bin_file.prerequisites, bin_file.prerequisites,
bin_file.name, bin_file.name,
@ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name) @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name),
lib_args,
lib_paths
) )
end end
@ -69,7 +73,7 @@ namespace BULLSEYE_SYM do
task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{BULLSEYE_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}") task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{BULLSEYE_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}")
desc 'Run code coverage for all tests' desc 'Run code coverage for all tests'
task all: [:directories] do task all: [:test_deps] do
@ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
@ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[BULLSEYE_SYM].enableBullseye(true)
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM)
@ -81,18 +85,18 @@ namespace BULLSEYE_SYM do
message = "\nOops! '#{BULLSEYE_ROOT_NAME}:*' isn't a real task. " + message = "\nOops! '#{BULLSEYE_ROOT_NAME}:*' isn't a real task. " +
"Use a real test or source file name (no path) in place of the wildcard.\n" + "Use a real test or source file name (no path) in place of the wildcard.\n" +
"Example: rake #{BULLSEYE_ROOT_NAME}:foo.c\n\n" "Example: rake #{BULLSEYE_ROOT_NAME}:foo.c\n\n"
@ceedling[:streaminator].stdout_puts( message ) @ceedling[:streaminator].stdout_puts( message )
end end
desc 'Run tests by matching regular expression pattern.' desc 'Run tests by matching regular expression pattern.'
task :pattern, [:regex] => [:directories] do |_t, args| task :pattern, [:regex] => [:test_deps] do |_t, args|
matches = [] matches = []
COLLECTION_ALL_TESTS.each do |test| COLLECTION_ALL_TESTS.each do |test|
matches << test if test =~ /#{args.regex}/ matches << test if test =~ /#{args.regex}/
end end
if !matches.empty? if !matches.empty?
@ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
@ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[BULLSEYE_SYM].enableBullseye(true)
@ -104,13 +108,13 @@ namespace BULLSEYE_SYM do
end end
desc 'Run tests whose test path contains [dir] or [dir] substring.' desc 'Run tests whose test path contains [dir] or [dir] substring.'
task :path, [:dir] => [:directories] do |_t, args| task :path, [:dir] => [:test_deps] do |_t, args|
matches = [] matches = []
COLLECTION_ALL_TESTS.each do |test| COLLECTION_ALL_TESTS.each do |test|
matches << test if File.dirname(test).include?(args.dir.tr('\\', '/')) matches << test if File.dirname(test).include?(args.dir.tr('\\', '/'))
end end
if !matches.empty? if !matches.empty?
@ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
@ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[BULLSEYE_SYM].enableBullseye(true)
@ -122,13 +126,13 @@ namespace BULLSEYE_SYM do
end end
desc 'Run code coverage for changed files' desc 'Run code coverage for changed files'
task delta: [:directories] do task delta: [:test_deps] do
@ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
@ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[BULLSEYE_SYM].enableBullseye(true)
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM, {:force_run => false}) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM, {:force_run => false})
@ceedling[:configurator].restore_config @ceedling[:configurator].restore_config
end end
# use a rule to increase efficiency for large projects # use a rule to increase efficiency for large projects
# bullseye test tasks by regex # bullseye test tasks by regex
rule(/^#{BULLSEYE_TASK_ROOT}\S+$/ => [ rule(/^#{BULLSEYE_TASK_ROOT}\S+$/ => [
@ -138,7 +142,7 @@ namespace BULLSEYE_SYM do
@ceedling[:file_finder].find_test_from_file_path(test) @ceedling[:file_finder].find_test_from_file_path(test)
end end
]) do |test| ]) do |test|
@ceedling[:rake_wrapper][:directories].invoke @ceedling[:rake_wrapper][:test_deps].invoke
@ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
@ceedling[BULLSEYE_SYM].enableBullseye(true) @ceedling[BULLSEYE_SYM].enableBullseye(true)
@ceedling[:test_invoker].setup_and_invoke([test.source], BULLSEYE_SYM) @ceedling[:test_invoker].setup_and_invoke([test.source], BULLSEYE_SYM)
@ -159,11 +163,11 @@ end
end end
namespace UTILS_SYM do namespace UTILS_SYM do
desc "Open Bullseye code coverage browser" desc "Open Bullseye code coverage browser"
task BULLSEYE_SYM do task BULLSEYE_SYM do
command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_BROWSER, []) command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_BROWSER, [])
@ceedling[:tool_executor].exec(command[:line], command[:options]) @ceedling[:tool_executor].exec(command[:line], command[:options])
end end
end end

View File

@ -7,95 +7,427 @@ Plugin for integrating GNU GCov code coverage tool into Ceedling projects.
Currently only designed for the gcov command (like LCOV for example). In the Currently only designed for the gcov command (like LCOV for example). In the
future we could configure this to work with other code coverage tools. future we could configure this to work with other code coverage tools.
This plugin currently uses `gcovr` to generate HTML and/or XML reports as a This plugin currently uses [gcovr](https://www.gcovr.com/) and / or
utility. The normal gcov plugin _must_ be run first for this report to generate. [ReportGenerator](https://danielpalme.github.io/ReportGenerator/)
as utilities to generate HTML, XML, JSON, or Text reports. The normal gcov
plugin _must_ be run first for these reports to generate.
## Installation ## Installation
Gcovr can be installed via pip like so: gcovr can be installed via pip like so:
``` ```sh
pip install gcovr pip install gcovr
``` ```
ReportGenerator can be installed via .NET Core like so:
```sh
dotnet tool install -g dotnet-reportgenerator-globaltool
```
It is not required to install both `gcovr` and `ReportGenerator`. Either utility
may be installed to create reports.
## Configuration ## Configuration
The gcov plugin supports configuration options via your `project.yml` provided The gcov plugin supports configuration options via your `project.yml` provided
by Ceedling. by Ceedling.
Generation of HTML reports may be enabled or disabled with the following ### Utilities
config. Set to `true` to enable or set to `false` to disable.
``` Gcovr and / or ReportGenerator may be enabled to create coverage reports.
```yaml
:gcov: :gcov:
:html_report: true :utilities:
- gcovr # Use gcovr to create the specified reports (default).
- ReportGenerator # Use ReportGenerator to create the specified reports.
``` ```
Generation of XML reports may be enabled or disabled with the following ### Reports
config. Set to `true` to enable or set to `false` to disable.
``` Various reports are available and may be enabled with the following
configuration item. See the specific report sections in this README
for additional options and information. All generated reports will be found in `build/artifacts/gcov`.
```yaml
:gcov: :gcov:
:xml_report: true # Specify one or more reports to generate.
# Defaults to HtmlBasic.
:reports:
# Make an HTML summary report.
# Supported utilities: gcovr, ReportGenerator
- HtmlBasic
# Make an HTML report with line by line coverage of each source file.
# Supported utilities: gcovr, ReportGenerator
- HtmlDetailed
# Make a Text report, which may be output to the console with gcovr or a file in both gcovr and ReportGenerator.
# Supported utilities: gcovr, ReportGenerator
- Text
# Make a Cobertura XML report.
# Supported utilities: gcovr, ReportGenerator
- Cobertura
# Make a SonarQube XML report.
# Supported utilities: gcovr, ReportGenerator
- SonarQube
# Make a JSON report.
# Supported utilities: gcovr
- JSON
# Make a detailed HTML report with CSS and JavaScript included in every HTML page. Useful for build servers.
# Supported utilities: ReportGenerator
- HtmlInline
# Make a detailed HTML report with a light theme and CSS and JavaScript included in every HTML page for Azure DevOps.
# Supported utilities: ReportGenerator
- HtmlInlineAzure
# Make a detailed HTML report with a dark theme and CSS and JavaScript included in every HTML page for Azure DevOps.
# Supported utilities: ReportGenerator
- HtmlInlineAzureDark
# Make a single HTML file containing a chart with historic coverage information.
# Supported utilities: ReportGenerator
- HtmlChart
# Make a detailed HTML report in a single file.
# Supported utilities: ReportGenerator
- MHtml
# Make SVG and PNG files that show line and / or branch coverage information.
# Supported utilities: ReportGenerator
- Badges
# Make a single CSV file containing coverage information per file.
# Supported utilities: ReportGenerator
- CsvSummary
# Make a single TEX file containing a summary for all files and detailed reports for each files.
# Supported utilities: ReportGenerator
- Latex
# Make a single TEX file containing a summary for all files.
# Supported utilities: ReportGenerator
- LatexSummary
# Make a single PNG file containing a chart with historic coverage information.
# Supported utilities: ReportGenerator
- PngChart
# Command line output interpreted by TeamCity.
# Supported utilities: ReportGenerator
- TeamCitySummary
# Make a text file in lcov format.
# Supported utilities: ReportGenerator
- lcov
# Make a XML file containing a summary for all classes and detailed reports for each class.
# Supported utilities: ReportGenerator
- Xml
# Make a single XML file containing a summary for all files.
# Supported utilities: ReportGenerator
- XmlSummary
``` ```
There are two types of gcovr HTML reports that can be configured in your ### Gcovr HTML Reports
`project.yml`. To create a basic HTML report, with only the overall file
information, use the following config.
``` Generation of Gcovr HTML reports may be modified with the following configuration items.
```yaml
:gcov: :gcov:
:html_report_type: basic # Set to 'true' to enable HTML reports or set to 'false' to disable.
# Defaults to enabled. (gcovr --html)
# Deprecated - See the :reports: configuration option.
:html_report: [true|false]
# Gcovr supports generating two types of HTML reports. Use 'basic' to create
# an HTML report with only the overall file information. Use 'detailed' to create
# an HTML report with line by line coverage of each source file.
# Defaults to 'basic'. Set to 'detailed' for (gcovr --html-details).
# Deprecated - See the :reports: configuration option.
:html_report_type: [basic|detailed]
:gcovr:
# HTML report filename.
:html_artifact_filename: <output>
# Use 'title' as title for the HTML report.
# Default is 'Head'. (gcovr --html-title)
:html_title: <title>
# If the coverage is below MEDIUM, the value is marked as low coverage in the HTML report.
# MEDIUM has to be lower than or equal to value of html_high_threshold.
# If MEDIUM is equal to value of html_high_threshold the report has only high and low coverage.
# Default is 75.0. (gcovr --html-medium-threshold)
:html_medium_threshold: 75
# If the coverage is below HIGH, the value is marked as medium coverage in the HTML report.
# HIGH has to be greater than or equal to value of html_medium_threshold.
# If HIGH is equal to value of html_medium_threshold the report has only high and low coverage.
# Default is 90.0. (gcovr -html-high-threshold)
:html_high_threshold: 90
# Set to 'true' to use absolute paths to link the 'detailed' reports.
# Defaults to relative links. (gcovr --html-absolute-paths)
:html_absolute_paths: [true|false]
# Override the declared HTML report encoding. Defaults to UTF-8. (gcovr --html-encoding)
:html_encoding: <html_encoding>
``` ```
To create a detailed HTML report, with line by line breakdown of the ### Cobertura XML Reports
coverage, use the following config.
``` Generation of Cobertura XML reports may be modified with the following configuration items.
```yaml
:gcov: :gcov:
:html_report_type: detailed # Set to 'true' to enable Cobertura XML reports or set to 'false' to disable.
# Defaults to disabled. (gcovr --xml)
# Deprecated - See the :reports: configuration option.
:xml_report: [true|false]
:gcovr:
# Set to 'true' to pretty-print the Cobertura XML report, otherwise set to 'false'.
# Defaults to disabled. (gcovr --xml-pretty)
:xml_pretty: [true|false]
:cobertura_pretty: [true|false]
# Cobertura XML report filename.
:xml_artifact_filename: <output>
:cobertura_artifact_filename: <output>
``` ```
### SonarQube XML Reports
Generation of SonarQube XML reports may be modified with the following configuration items.
```yaml
:gcov:
:gcovr:
# SonarQube XML report filename.
:sonarqube_artifact_filename: <output>
```
### JSON Reports
Generation of JSON reports may be modified with the following configuration items.
```yaml
:gcov:
:gcovr:
# Set to 'true' to pretty-print the JSON report, otherwise set 'false'.
# Defaults to disabled. (gcovr --json-pretty)
:json_pretty: [true|false]
# JSON report filename.
:json_artifact_filename: <output>
```
### Text Reports
Generation of text reports may be modified with the following configuration items.
Text reports may be printed to the console or output to a file.
```yaml
:gcov:
:gcovr:
# Text report filename.
# The text report is printed to the console when no filename is provided.
:text_artifact_filename: <output>
```
### Common Report Options
There are a number of options to control which files are considered part of There are a number of options to control which files are considered part of
the coverage report. Most often, we only care about coverage on our source code, and not the coverage report. Most often, we only care about coverage on our source code, and not
on tests or automatically generated mocks, runners, etc. However, there are times on tests or automatically generated mocks, runners, etc. However, there are times
where this isn't true... or there are times where we've moved ceedling's directory where this isn't true... or there are times where we've moved ceedling's directory
structure so that the project file isn't at the root of the project anymore. In these structure so that the project file isn't at the root of the project anymore. In these
cases, you may need to tweak the following: cases, you may need to tweak `report_include`, `report_exclude`, and `exclude_directories`.
``` One important note about `report_root`: gcovr will take only a single root folder, unlike
:gcov: Ceedling's ability to take as many as you like. So you will need to choose a folder which is
:report_root: "."
:report_exclude: "^build|^vendor|^test|^support"
:report_include: "^src"
```
One important note about html_report_root: gcovr will only take a single root folder, unlike
Ceedling's ability to take as many as you like. So you will need to choose a folder which is
a superset of ALL the folders you want, and then use the include or exclude options to set up a superset of ALL the folders you want, and then use the include or exclude options to set up
patterns of files to pay attention to or ignore. It's not ideal, but it works. patterns of files to pay attention to or ignore. It's not ideal, but it works.
Finally, there are a number of settings which can be specified in order to adjust the Finally, there are a number of settings which can be specified to adjust the
default behaviors of gcov: default behaviors of gcovr:
``` ```yaml
:gcov: :gcov:
:html_medium_threshold: 75 :gcovr:
:html_high_threshold: 90 # The root directory of your source files. Defaults to ".", the current directory.
:fail_under_line: 30 # File names are reported relative to this root. The report_root is the default report_include.
:fail_under_branch: 30 :report_root: "."
# Load the specified configuration file.
# Defaults to gcovr.cfg in the report_root directory. (gcovr --config)
:config_file: <config_file>
# Exit with a status of 2 if the total line coverage is less than MIN.
# Can be ORed with exit status of 'fail_under_branch' option. (gcovr --fail-under-line)
:fail_under_line: 30
# Exit with a status of 4 if the total branch coverage is less than MIN.
# Can be ORed with exit status of 'fail_under_line' option. (gcovr --fail-under-branch)
:fail_under_branch: 30
# Select the source file encoding.
# Defaults to the system default encoding (UTF-8). (gcovr --source-encoding)
:source_encoding: <source_encoding>
# Report the branch coverage instead of the line coverage. For text report only. (gcovr --branches).
:branches: [true|false]
# Sort entries by increasing number of uncovered lines.
# For text and HTML report. (gcovr --sort-uncovered)
:sort_uncovered: [true|false]
# Sort entries by increasing percentage of uncovered lines.
# For text and HTML report. (gcovr --sort-percentage)
:sort_percentage: [true|false]
# Print a small report to stdout with line & branch percentage coverage.
# This is in addition to other reports. (gcovr --print-summary).
:print_summary: [true|false]
# Keep only source files that match this filter. (gcovr --filter).
:report_include: "^src"
# Exclude source files that match this filter. (gcovr --exclude).
:report_exclude: "^vendor.*|^build.*|^test.*|^lib.*"
# Keep only gcov data files that match this filter. (gcovr --gcov-filter).
:gcov_filter: <gcov_filter>
# Exclude gcov data files that match this filter. (gcovr --gcov-exclude).
:gcov_exclude: <gcov_exclude>
# Exclude directories that match this regex while searching
# raw coverage files. (gcovr --exclude-directories).
:exclude_directories: <exclude_dirs>
# Use a particular gcov executable. (gcovr --gcov-executable).
:gcov_executable: <gcov_cmd>
# Exclude branch coverage from lines without useful
# source code. (gcovr --exclude-unreachable-branches).
:exclude_unreachable_branches: [true|false]
# For branch coverage, exclude branches that the compiler
# generates for exception handling. (gcovr --exclude-throw-branches).
:exclude_throw_branches: [true|false]
# Use existing gcov files for analysis. Default: False. (gcovr --use-gcov-files)
:use_gcov_files: [true|false]
# Skip lines with parse errors in GCOV files instead of
# exiting with an error. (gcovr --gcov-ignore-parse-errors).
:gcov_ignore_parse_errors: [true|false]
# Override normal working directory detection. (gcovr --object-directory)
:object_directory: <objdir>
# Keep gcov files after processing. (gcovr --keep).
:keep: [true|false]
# Delete gcda files after processing. (gcovr --delete).
:delete: [true|false]
# Set the number of threads to use in parallel. (gcovr -j).
:num_parallel_threads: <num_threads>
# When scanning the code coverage, if any files are found that do not have
# associated coverage data, the command will abort with an error message.
:abort_on_uncovered: true
# When using the ``abort_on_uncovered`` option, the files in this list will not
# trigger a failure.
# Ceedling globs described in the Ceedling packet ``Path`` section can be used
# when directories are placed on the list. Globs are limited to matching directories
# and not files.
:uncovered_ignore_list: []
``` ```
These HTML and XML reports will be found in `build/artifacts/gcov`. ### ReportGenerator Configuration
The ReportGenerator utility may be configured with the following configuration items.
All generated reports may be found in `build/artifacts/gcov/ReportGenerator`.
```yaml
:gcov:
:report_generator:
# Optional directory for storing persistent coverage information.
# Can be used in future reports to show coverage evolution.
:history_directory: <history_directory>
# Optional plugin files for custom reports or custom history storage (separated by semicolon).
:plugins: CustomReports.dll
# Optional list of assemblies that should be included or excluded in the report (separated by semicolon)..
# Exclusion filters take precedence over inclusion filters.
# Wildcards are allowed, but not regular expressions.
:assembly_filters: "+Included;-Excluded"
# Optional list of classes that should be included or excluded in the report (separated by semicolon)..
# Exclusion filters take precedence over inclusion filters.
# Wildcards are allowed, but not regular expressions.
:class_filters: "+Included;-Excluded"
# Optional list of files that should be included or excluded in the report (separated by semicolon)..
# Exclusion filters take precedence over inclusion filters.
# Wildcards are allowed, but not regular expressions.
:file_filters: "-./vendor/*;-./build/*;-./test/*;-./lib/*;+./src/*"
# The verbosity level of the log messages.
# Values: Verbose, Info, Warning, Error, Off
:verbosity: Warning
# Optional tag or build version.
:tag: <tag>
# Optional list of one or more regular expressions to exclude gcov notes files that match these filters.
:gcov_exclude:
- <exclude_regex1>
- <exclude_regex2>
# Optionally use a particular gcov executable. Defaults to gcov.
:gcov_executable: <gcov_cmd>
# Optionally set the number of threads to use in parallel. Defaults to 1.
:num_parallel_threads: <num_threads>
# Optional list of one or more command line arguments to pass to Report Generator.
# Useful for configuring Risk Hotspots and Other Settings.
# https://github.com/danielpalme/ReportGenerator/wiki/Settings
:custom_args:
- <custom_arg1>
- <custom_arg2>
```
## Example Usage ## Example Usage
``` ```sh
ceedling gcov:all utils:gcov ceedling gcov:all utils:gcov
``` ```
## To-Do list ## To-Do list
- Generate overall report (combined statistics from all files with coverage) - Generate overall report (combined statistics from all files with coverage)
- Generate coverage output files
- Easier option override for better customisation ## Citations
Most of the comment text which describes the options was taken from the
[Gcovr User Guide](https://www.gcovr.com/en/stable/guide.html) and the
[ReportGenerator Wiki](https://github.com/danielpalme/ReportGenerator/wiki).
The text is repeated here to provide the most accurate option functionality.

View File

@ -1,73 +0,0 @@
---
:tools:
:gcov_compiler:
:executable: gcc
:arguments:
- -g
- -fprofile-arcs
- -ftest-coverage
- -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
- -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
- -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
- -DGCOV_COMPILER
- -DCODE_COVERAGE
- -c "${1}"
- -o "${2}"
:gcov_linker:
:executable: gcc
:arguments:
- -fprofile-arcs
- -ftest-coverage
- ${1}
- -o ${2}
- ${3}
:gcov_fixture:
:executable: ${1}
:gcov_report:
:executable: gcov
:arguments:
- -n
- -p
- -b
- -o "$": GCOV_BUILD_OUTPUT_PATH
- "\"${1}\""
:gcov_post_report:
:executable: gcovr
:optional: TRUE
:arguments:
- -p
- -b
- ${1}
- --html
- -o GcovCoverageResults.html
:gcov_post_report_basic:
:executable: gcovr
:optional: TRUE
:arguments:
- -p
- -b
- ${1}
- --html
- -o "$": GCOV_ARTIFACTS_FILE
:gcov_post_report_advanced:
:executable: gcovr
:optional: TRUE
:arguments:
- -p
- -b
- ${1}
- --html
- --html-details
- -o "$": GCOV_ARTIFACTS_FILE
:gcov_post_report_xml:
:executable: gcovr
:optional: TRUE
:arguments:
- -p
- -b
- ${1}
- --xml
- -o "$": GCOV_ARTIFACTS_FILE_XML
...

View File

@ -1,3 +1,6 @@
require 'reportgenerator_reportinator'
require 'gcovr_reportinator'
directory(GCOV_BUILD_OUTPUT_PATH) directory(GCOV_BUILD_OUTPUT_PATH)
directory(GCOV_RESULTS_PATH) directory(GCOV_RESULTS_PATH)
directory(GCOV_ARTIFACTS_PATH) directory(GCOV_ARTIFACTS_PATH)
@ -16,7 +19,7 @@ rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_OBJECT}$/ => [
end end
]) do |object| ]) do |object|
if File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX}|#{GCOV_IGNORE_SOURCES.join('|')})/i if File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX})|(#{VENDORS_FILES.map{|source| '\b' + source + '\b'}.join('|')})/
@ceedling[:generator].generate_object_file( @ceedling[:generator].generate_object_file(
TOOLS_GCOV_COMPILER, TOOLS_GCOV_COMPILER,
OPERATION_COMPILE_SYM, OPERATION_COMPILE_SYM,
@ -32,14 +35,15 @@ end
rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_EXECUTABLE}$/) do |bin_file| rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_EXECUTABLE}$/) do |bin_file|
lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments() lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments()
lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments()
@ceedling[:generator].generate_executable_file( @ceedling[:generator].generate_executable_file(
TOOLS_GCOV_LINKER, TOOLS_GCOV_LINKER,
GCOV_SYM, GCOV_SYM,
bin_file.prerequisites, bin_file.prerequisites,
bin_file.name, bin_file.name,
@ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name),
lib_args, lib_args,
@ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name) lib_paths
) )
end end
@ -71,7 +75,7 @@ namespace GCOV_SYM do
task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{GCOV_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}") task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{GCOV_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}")
desc 'Run code coverage for all tests' desc 'Run code coverage for all tests'
task all: [:directories] do task all: [:test_deps] do
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM)
@ceedling[:configurator].restore_config @ceedling[:configurator].restore_config
@ -87,7 +91,7 @@ namespace GCOV_SYM do
end end
desc 'Run tests by matching regular expression pattern.' desc 'Run tests by matching regular expression pattern.'
task :pattern, [:regex] => [:directories] do |_t, args| task :pattern, [:regex] => [:test_deps] do |_t, args|
matches = [] matches = []
COLLECTION_ALL_TESTS.each do |test| COLLECTION_ALL_TESTS.each do |test|
@ -104,7 +108,7 @@ namespace GCOV_SYM do
end end
desc 'Run tests whose test path contains [dir] or [dir] substring.' desc 'Run tests whose test path contains [dir] or [dir] substring.'
task :path, [:dir] => [:directories] do |_t, args| task :path, [:dir] => [:test_deps] do |_t, args|
matches = [] matches = []
COLLECTION_ALL_TESTS.each do |test| COLLECTION_ALL_TESTS.each do |test|
@ -121,7 +125,7 @@ namespace GCOV_SYM do
end end
desc 'Run code coverage for changed files' desc 'Run code coverage for changed files'
task delta: [:directories] do task delta: [:test_deps] do
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM, force_run: false) @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM, force_run: false)
@ceedling[:configurator].restore_config @ceedling[:configurator].restore_config
@ -136,7 +140,7 @@ namespace GCOV_SYM do
@ceedling[:file_finder].find_test_from_file_path(test) @ceedling[:file_finder].find_test_from_file_path(test)
end end
]) do |test| ]) do |test|
@ceedling[:rake_wrapper][:directories].invoke @ceedling[:rake_wrapper][:test_deps].invoke
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config) @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke([test.source], GCOV_SYM) @ceedling[:test_invoker].setup_and_invoke([test.source], GCOV_SYM)
@ceedling[:configurator].restore_config @ceedling[:configurator].restore_config
@ -154,67 +158,52 @@ if PROJECT_USE_DEEP_DEPENDENCIES
end end
namespace UTILS_SYM do namespace UTILS_SYM do
def gcov_args_builder(opts) # Report Creation Utilities
args = "" UTILITY_NAME_GCOVR = "gcovr"
args += "-r \"#{opts[:gcov_report_root] || '.'}\" " UTILITY_NAME_REPORT_GENERATOR = "ReportGenerator"
args += "-f \"#{opts[:gcov_report_include]}\" " unless opts[:gcov_report_include].nil? UTILITY_NAMES = [UTILITY_NAME_GCOVR, UTILITY_NAME_REPORT_GENERATOR]
args += "-e \"#{opts[:gcov_report_exclude] || GCOV_FILTER_EXCLUDE}\" "
[ :gcov_fail_under_line, :gcov_fail_under_branch, :gcov_html_medium_threshold, :gcov_html_high_threshold].each do |opt| # Returns true is the given utility is enabled, otherwise returns false.
args += "--#{opt.to_s.gsub('_','-').sub(/:?gcov-/,'')} #{opts[opt]} " unless opts[opt].nil? def is_utility_enabled(opts, utility_name)
end return !(opts.nil?) && !(opts[:gcov_utilities].nil?) && (opts[:gcov_utilities].map(&:upcase).include? utility_name.upcase)
return args
end end
desc 'Create gcov code coverage html report (must run ceedling gcov first)'
task GCOV_SYM do
desc "Create gcov code coverage html/xml/json/text report(s). (Note: Must run 'ceedling gcov' first)."
task GCOV_SYM do
# Get the gcov options from project.yml.
opts = @ceedling[:configurator].project_config_hash
# Create the artifacts output directory.
if !File.directory? GCOV_ARTIFACTS_PATH if !File.directory? GCOV_ARTIFACTS_PATH
FileUtils.mkdir_p GCOV_ARTIFACTS_PATH FileUtils.mkdir_p GCOV_ARTIFACTS_PATH
end end
args = gcov_args_builder(@ceedling[:configurator].project_config_hash) # Remove unsupported reporting utilities.
if !(opts[:gcov_utilities].nil?)
if @ceedling[:configurator].project_config_hash[:gcov_html_report].nil? opts[:gcov_utilities].reject! { |item| !(UTILITY_NAMES.map(&:upcase).include? item.upcase) }
puts "In your project.yml, define: \n\n:gcov:\n :html_report:\n\n to true or false to refine this feature."
puts "For now, assumimg you want an html report generated."
html_enabled = true
else
html_enabled = @ceedling[:configurator].project_config_hash[:gcov_html_report]
end end
if @ceedling[:configurator].project_config_hash[:gcov_xml_report].nil? # Default to gcovr when no reporting utilities are specified.
puts "In your project.yml, define: \n\n:gcov:\n :xml_report:\n\n to true or false to refine this feature." if opts[:gcov_utilities].nil? || opts[:gcov_utilities].empty?
puts "For now, assumimg you do not want an xml report generated." opts[:gcov_utilities] = [UTILITY_NAME_GCOVR]
xml_enabled = false
else
xml_enabled = @ceedling[:configurator].project_config_hash[:gcov_xml_report]
end end
if html_enabled if opts[:gcov_reports].nil?
if @ceedling[:configurator].project_config_hash[:gcov_html_report_type] == 'basic' opts[:gcov_reports] = []
puts "Creating a basic html report of gcov results in #{GCOV_ARTIFACTS_FILE}..."
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_BASIC, [], args)
@ceedling[:tool_executor].exec(command[:line], command[:options])
elsif @ceedling[:configurator].project_config_hash[:gcov_html_report_type] == 'detailed'
puts "Creating a detailed html report of gcov results in #{GCOV_ARTIFACTS_FILE}..."
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_ADVANCED, [], args)
@ceedling[:tool_executor].exec(command[:line], command[:options])
else
puts "In your project.yml, define: \n\n:gcov:\n :html_report_type:\n\n to basic or detailed to refine this feature."
puts "For now, just creating basic."
puts "Creating a basic html report of gcov results in #{GCOV_ARTIFACTS_FILE}..."
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_BASIC, [], args)
@ceedling[:tool_executor].exec(command[:line], command[:options])
end
end end
if xml_enabled gcovr_reportinator = GcovrReportinator.new(@ceedling)
puts "Creating an xml report of gcov results in #{GCOV_ARTIFACTS_FILE_XML}..." gcovr_reportinator.support_deprecated_options(opts)
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_XML, [], filter)
@ceedling[:tool_executor].exec(command[:line], command[:options]) if is_utility_enabled(opts, UTILITY_NAME_GCOVR)
gcovr_reportinator.make_reports(opts)
end
if is_utility_enabled(opts, UTILITY_NAME_REPORT_GENERATOR)
reportgenerator_reportinator = ReportGeneratorReportinator.new(@ceedling)
reportgenerator_reportinator.make_reports(opts)
end end
puts "Done."
end end
end end

View File

@ -82,10 +82,7 @@ class Gcov < Plugin
banner = @ceedling[:plugin_reportinator].generate_banner "#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY" banner = @ceedling[:plugin_reportinator].generate_banner "#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY"
@ceedling[:streaminator].stdout_puts "\n" + banner @ceedling[:streaminator].stdout_puts "\n" + banner
coverage_sources = sources.clone coverage_sources = @ceedling[:project_config_manager].filter_internal_sources(sources)
coverage_sources.delete_if { |item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{EXTENSION_SOURCE}$/ }
coverage_sources.delete_if { |item| item =~ /#{GCOV_IGNORE_SOURCES.join('|')}#{EXTENSION_SOURCE}$/ }
coverage_sources.each do |source| coverage_sources.each do |source|
basename = File.basename(source) basename = File.basename(source)
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORT, [], [basename]) command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORT, [], [basename])
@ -98,9 +95,35 @@ class Gcov < Plugin
end end
end end
ignore_path_list = @ceedling[:file_system_utils].collect_paths(@ceedling[:configurator].project_config_hash[:gcov_uncovered_ignore_list] || [])
ignore_uncovered_list = @ceedling[:file_wrapper].instantiate_file_list
ignore_path_list.each do |path|
if File.exists?(path) and not File.directory?(path)
ignore_uncovered_list.include(path)
else
ignore_uncovered_list.include(File.join(path, "*#{EXTENSION_SOURCE}"))
end
end
found_uncovered = false
COLLECTION_ALL_SOURCE.each do |source| COLLECTION_ALL_SOURCE.each do |source|
unless coverage_sources.include?(source) unless coverage_sources.include?(source)
@ceedling[:streaminator].stdout_puts("Could not find coverage results for " + source + "\n") v = Verbosity::DEBUG
msg = "Could not find coverage results for " + source
if ignore_uncovered_list.include?(source)
msg += " [IGNORED]"
else
found_uncovered = true
v = Verbosity::NORMAL
end
msg += "\n"
@ceedling[:streaminator].stdout_puts(msg, v)
end
end
if found_uncovered
if @ceedling[:configurator].project_config_hash[:gcov_abort_on_uncovered]
@ceedling[:streaminator].stderr_puts("There were files with no coverage results: aborting.\n")
exit(-1)
end end
end end
end end

View File

@ -1,19 +1,48 @@
GCOV_ROOT_NAME = 'gcov'.freeze GCOV_ROOT_NAME = 'gcov'.freeze
GCOV_TASK_ROOT = GCOV_ROOT_NAME + ':' GCOV_TASK_ROOT = GCOV_ROOT_NAME + ':'
GCOV_SYM = GCOV_ROOT_NAME.to_sym GCOV_SYM = GCOV_ROOT_NAME.to_sym
GCOV_BUILD_PATH = File.join(PROJECT_BUILD_ROOT, GCOV_ROOT_NAME) GCOV_BUILD_PATH = File.join(PROJECT_BUILD_ROOT, GCOV_ROOT_NAME)
GCOV_BUILD_OUTPUT_PATH = File.join(GCOV_BUILD_PATH, "out") GCOV_BUILD_OUTPUT_PATH = File.join(GCOV_BUILD_PATH, "out")
GCOV_RESULTS_PATH = File.join(GCOV_BUILD_PATH, "results") GCOV_RESULTS_PATH = File.join(GCOV_BUILD_PATH, "results")
GCOV_DEPENDENCIES_PATH = File.join(GCOV_BUILD_PATH, "dependencies") GCOV_DEPENDENCIES_PATH = File.join(GCOV_BUILD_PATH, "dependencies")
GCOV_ARTIFACTS_PATH = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, GCOV_ROOT_NAME) GCOV_ARTIFACTS_PATH = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, GCOV_ROOT_NAME)
GCOV_REPORT_GENERATOR_PATH = File.join(GCOV_ARTIFACTS_PATH, "ReportGenerator")
GCOV_ARTIFACTS_FILE = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.html") GCOV_ARTIFACTS_FILE_HTML = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.html")
GCOV_ARTIFACTS_FILE_XML = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.xml") GCOV_ARTIFACTS_FILE_COBERTURA = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageCobertura.xml")
GCOV_ARTIFACTS_FILE_SONARQUBE = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageSonarQube.xml")
GCOV_ARTIFACTS_FILE_JSON = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverage.json")
GCOV_IGNORE_SOURCES = %w(unity cmock cexception).freeze GCOV_FILTER_EXCLUDE_PATHS = ['vendor', 'build', 'test', 'lib']
GCOV_FILTER_EXCLUDE = '^vendor.*|^build.*|^test.*|^lib.*' # gcovr supports regular expressions.
GCOV_FILTER_EXCLUDE = GCOV_FILTER_EXCLUDE_PATHS.map{|path| '^'.concat(*path).concat('.*')}.join('|')
# ReportGenerator supports text with wildcard characters.
GCOV_REPORT_GENERATOR_FILE_FILTERS = GCOV_FILTER_EXCLUDE_PATHS.map{|path| File.join('-.', *path, '*')}.join(';')
# Report Types
class ReportTypes
HTML_BASIC = "HtmlBasic"
HTML_DETAILED = "HtmlDetailed"
HTML_CHART = "HtmlChart"
HTML_INLINE = "HtmlInline"
HTML_INLINE_AZURE = "HtmlInlineAzure"
HTML_INLINE_AZURE_DARK = "HtmlInlineAzureDark"
MHTML = "MHtml"
TEXT = "Text"
COBERTURA = "Cobertura"
SONARQUBE = "SonarQube"
JSON = "JSON"
BADGES = "Badges"
CSV_SUMMARY = "CsvSummary"
LATEX = "Latex"
LATEX_SUMMARY = "LatexSummary"
PNG_CHART = "PngChart"
TEAM_CITY_SUMMARY = "TeamCitySummary"
LCOV = "lcov"
XML = "Xml"
XML_SUMMARY = "XmlSummary"
end

View File

@ -44,7 +44,7 @@ class JunitTestsReport < Plugin
def write_header( results, stream ) def write_header( results, stream )
results[:counts][:time] = @time_result.reduce(0, :+) results[:counts][:time] = @time_result.reduce(0, :+)
stream.puts '<?xml version="1.0" encoding="utf-8" ?>' stream.puts '<?xml version="1.0" encoding="utf-8" ?>'
stream.puts('<testsuites tests="%<total>d" failures="%<failed>d" skipped="%<ignored>d" time="%<time>f">' % results[:counts]) stream.puts('<testsuites tests="%<total>d" failures="%<failed>d" time="%<time>.3f">' % results[:counts])
end end
def write_footer( stream ) def write_footer( stream )
@ -53,7 +53,7 @@ class JunitTestsReport < Plugin
def reorganise_results( results ) def reorganise_results( results )
# Reorganise the output by test suite instead of by result # Reorganise the output by test suite instead of by result
suites = Hash.new{ |h,k| h[k] = {collection: [], total: 0, success: 0, failed: 0, ignored: 0, stdout: []} } suites = Hash.new{ |h,k| h[k] = {collection: [], total: 0, success: 0, failed: 0, ignored: 0, errors: 0, stdout: []} }
results[:successes].each do |result| results[:successes].each do |result|
source = result[:source] source = result[:source]
name = source[:file].sub(/\..{1,4}$/, "") name = source[:file].sub(/\..{1,4}$/, "")
@ -85,7 +85,7 @@ class JunitTestsReport < Plugin
def write_suite( suite, stream ) def write_suite( suite, stream )
suite[:time] = @time_result.shift suite[:time] = @time_result.shift
stream.puts(' <testsuite name="%<name>s" tests="%<total>d" failures="%<failed>d" skipped="%<ignored>d" time="%<time>f">' % suite) stream.puts(' <testsuite name="%<name>s" tests="%<total>d" failures="%<failed>d" skipped="%<ignored>d" errors="%<errors>d" time="%<time>.3f">' % suite)
suite[:collection].each do |test| suite[:collection].each do |test|
write_test( test, stream ) write_test( test, stream )
@ -108,12 +108,17 @@ class JunitTestsReport < Plugin
end end
def write_test( test, stream ) def write_test( test, stream )
test[:test].gsub!('"', '&quot;') test[:test].gsub!(/&/, '&amp;')
test[:test].gsub!(/</, '&lt;')
test[:test].gsub!(/>/, '&gt;')
test[:test].gsub!(/"/, '&quot;')
test[:test].gsub!(/'/, '&apos;')
case test[:result] case test[:result]
when :success when :success
stream.puts(' <testcase name="%<test>s" />' % test) stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f"/>' % test)
when :failed when :failed
stream.puts(' <testcase name="%<test>s">' % test) stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f">' % test)
if test[:message].empty? if test[:message].empty?
stream.puts(' <failure />') stream.puts(' <failure />')
else else
@ -121,7 +126,7 @@ class JunitTestsReport < Plugin
end end
stream.puts(' </testcase>') stream.puts(' </testcase>')
when :ignored when :ignored
stream.puts(' <testcase name="%<test>s">' % test) stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>.3f">' % test)
stream.puts(' <skipped />') stream.puts(' <skipped />')
stream.puts(' </testcase>') stream.puts(' </testcase>')
end end

View File

@ -18,6 +18,14 @@ class ModuleGenerator < Plugin
end end
end end
def stub_from_header(module_name, optz={})
require "cmock.rb" #From CMock
stuboptz = divine_options(optz)
pathname = optz[:path_inc] || optz[:path_src] || "src"
filename = File.expand_path(optz[:module_root_path], File.join(pathname, module_name + ".h"))
CMock.new(stuboptz).setup_skeletons(filename)
end
private private
def divine_options(optz={}) def divine_options(optz={})
@ -37,6 +45,8 @@ class ModuleGenerator < Plugin
:boilerplates => ((defined? MODULE_GENERATOR_BOILERPLATES) ? MODULE_GENERATOR_BOILERPLATES : {} ), :boilerplates => ((defined? MODULE_GENERATOR_BOILERPLATES) ? MODULE_GENERATOR_BOILERPLATES : {} ),
:naming => ((defined? MODULE_GENERATOR_NAMING ) ? MODULE_GENERATOR_NAMING : nil ), :naming => ((defined? MODULE_GENERATOR_NAMING ) ? MODULE_GENERATOR_NAMING : nil ),
:update_svn => ((defined? MODULE_GENERATOR_UPDATE_SVN ) ? MODULE_GENERATOR_UPDATE_SVN : false ), :update_svn => ((defined? MODULE_GENERATOR_UPDATE_SVN ) ? MODULE_GENERATOR_UPDATE_SVN : false ),
:skeleton_path=> ((defined? MODULE_GENERATOR_SOURCE_ROOT ) ? MODULE_GENERATOR_SOURCE_ROOT.gsub('\\', '/').sub(/^\//, '').sub(/\/$/, '') : "src" ),
:test_define => ((defined? MODULE_GENERATOR_TEST_DEFINE ) ? MODULE_GENERATOR_TEST_DEFINE : "TEST" ),
} }
# Read Boilerplate template file. # Read Boilerplate template file.
@ -44,15 +54,15 @@ class ModuleGenerator < Plugin
bf = MODULE_GENERATOR_BOILERPLATE_FILES bf = MODULE_GENERATOR_BOILERPLATE_FILES
if !bf[:src].nil? && File.exists?(bf[:src]) if !bf[:src].nil? && File.exists?(bf[:src])
unity_generator_options[:boilerplates][:src] = File.read(bf[:src]) unity_generator_options[:boilerplates][:src] = File.read(bf[:src])
end end
if !bf[:inc].nil? && File.exists?(bf[:inc]) if !bf[:inc].nil? && File.exists?(bf[:inc])
unity_generator_options[:boilerplates][:inc] = File.read(bf[:inc]) unity_generator_options[:boilerplates][:inc] = File.read(bf[:inc])
end end
if !bf[:tst].nil? && File.exists?(bf[:tst]) if !bf[:tst].nil? && File.exists?(bf[:tst])
unity_generator_options[:boilerplates][:tst] = File.read(bf[:tst]) unity_generator_options[:boilerplates][:tst] = File.read(bf[:tst])
end end
end end

View File

@ -26,6 +26,21 @@ namespace :module do
end end
end end
desc "Generate module stubs from header"
task :stub, :module_path do |t, args|
files = [args[:module_path]] + (args.extras || [])
optz = { :module_root_path => "" }
files.each do |v|
module_root_path, module_name = v.split(module_root_separator, 2)
if module_name
optz[:module_root_path] = module_root_path
v = module_name
end
# Otherwise, go through the normal procedure
@ceedling[:module_generator].stub_from_header(v, optz)
end
end
desc "Destroy module (source, header and test files)" desc "Destroy module (source, header and test files)"
task :destroy, :module_path do |t, args| task :destroy, :module_path do |t, args|
files = [args[:module_path]] + (args.extras || []) files = [args[:module_path]] + (args.extras || [])

View File

@ -20,7 +20,7 @@ class Subprojects < Plugin
end end
end end
#gather information about the subprojects # Gather information about the subprojects
@subprojects = {} @subprojects = {}
@subproject_lookup_by_path = {} @subproject_lookup_by_path = {}
SUBPROJECTS_PATHS.each do |subproj| SUBPROJECTS_PATHS.each do |subproj|
@ -84,7 +84,7 @@ class Subprojects < Plugin
Object.send(:remove_const, constant.to_sym) if (Object.const_defined? constant) Object.send(:remove_const, constant.to_sym) if (Object.const_defined? constant)
Object.const_set(constant, new_value) Object.const_set(constant, new_value)
end end
end end
# end blocks always executed following rake run # end blocks always executed following rake run

View File

@ -1,5 +1,5 @@
xml_tests_report xml_tests_report
==================== ================
## Overview ## Overview

View File

@ -11,7 +11,7 @@ extern "C"
#define CEXCEPTION_VERSION_MAJOR 1 #define CEXCEPTION_VERSION_MAJOR 1
#define CEXCEPTION_VERSION_MINOR 3 #define CEXCEPTION_VERSION_MINOR 3
#define CEXCEPTION_VERSION_BUILD 2 #define CEXCEPTION_VERSION_BUILD 3
#define CEXCEPTION_VERSION ((CEXCEPTION_VERSION_MAJOR << 16) | (CEXCEPTION_VERSION_MINOR << 8) | CEXCEPTION_VERSION_BUILD) #define CEXCEPTION_VERSION ((CEXCEPTION_VERSION_MAJOR << 16) | (CEXCEPTION_VERSION_MINOR << 8) | CEXCEPTION_VERSION_BUILD)
//To Use CException, you have a number of options: //To Use CException, you have a number of options:

View File

@ -1,2 +0,0 @@
18

View File

@ -1,2 +0,0 @@
1.3.1

View File

@ -2,13 +2,11 @@
# CMock Project - Automatic Mock Generation for C # CMock Project - Automatic Mock Generation for C
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
# [Released under MIT License. Please refer to license.txt for details] # [Released under MIT License. Please refer to license.txt for details]
# ========================================== # ==========================================
# Setup our load path: # Setup our load path:
[ [
'lib', 'lib'
].each do |dir| ].each do |dir|
$LOAD_PATH.unshift( File.join( File.expand_path(File.dirname(__FILE__)) + '/../', dir) ) $:.unshift(File.join(__dir__ + '/../', dir))
end end

View File

@ -12,5 +12,5 @@
'./vendor/unity/auto/', './vendor/unity/auto/',
'./test/system/' './test/system/'
].each do |dir| ].each do |dir|
$LOAD_PATH.unshift( File.join( File.expand_path(File.dirname(__FILE__) + "/../"), dir) ) $:.unshift(File.join(File.expand_path(File.dirname(__FILE__) + '/../'), dir))
end end

View File

@ -4,49 +4,63 @@
# [Released under MIT License. Please refer to license.txt for details] # [Released under MIT License. Please refer to license.txt for details]
# ========================================== # ==========================================
[ "../config/production_environment", ['../config/production_environment',
"cmock_header_parser", 'cmock_header_parser',
"cmock_generator", 'cmock_generator',
"cmock_file_writer", 'cmock_file_writer',
"cmock_config", 'cmock_config',
"cmock_plugin_manager", 'cmock_plugin_manager',
"cmock_generator_utils", 'cmock_generator_utils',
"cmock_unityhelper_parser"].each {|req| require "#{File.expand_path(File.dirname(__FILE__))}/#{req}"} 'cmock_unityhelper_parser'].each { |req| require "#{__dir__}/#{req}" }
class CMock class CMock
def initialize(options = nil)
def initialize(options=nil)
cm_config = CMockConfig.new(options) cm_config = CMockConfig.new(options)
cm_unityhelper = CMockUnityHelperParser.new(cm_config) cm_unityhelper = CMockUnityHelperParser.new(cm_config)
cm_writer = CMockFileWriter.new(cm_config) cm_writer = CMockFileWriter.new(cm_config)
cm_gen_utils = CMockGeneratorUtils.new(cm_config, {:unity_helper => cm_unityhelper}) cm_gen_utils = CMockGeneratorUtils.new(cm_config,
:unity_helper => cm_unityhelper)
cm_gen_plugins = CMockPluginManager.new(cm_config, cm_gen_utils) cm_gen_plugins = CMockPluginManager.new(cm_config, cm_gen_utils)
@cm_parser = CMockHeaderParser.new(cm_config) @cm_parser = CMockHeaderParser.new(cm_config)
@cm_generator = CMockGenerator.new(cm_config, cm_writer, cm_gen_utils, cm_gen_plugins) @cm_generator = CMockGenerator.new(cm_config, cm_writer, cm_gen_utils,
cm_gen_plugins)
@silent = (cm_config.verbosity < 2) @silent = (cm_config.verbosity < 2)
end end
def setup_mocks(files) def setup_mocks(files, folder = nil)
[files].flatten.each do |src| [files].flatten.each do |src|
generate_mock src generate_mock(src, folder)
end
end
def setup_skeletons(files)
[files].flatten.each do |src|
generate_skeleton src
end end
end end
private ############################### private ###############################
def generate_mock(src) def generate_mock(src, folder)
name = File.basename(src, '.h') name = File.basename(src, '.*')
ext = File.extname(src)
puts "Creating mock for #{name}..." unless @silent puts "Creating mock for #{name}..." unless @silent
@cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src))) @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)), ext, folder)
end
def generate_skeleton(src)
name = File.basename(src, '.*')
puts "Creating skeleton for #{name}..." unless @silent
@cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src)))
end end
end end
def option_maker(options, key, val) def option_maker(options, key, val)
options = options || {} options ||= {}
options[key.to_sym] = options[key.to_sym] =
if val.chr == ":" if val.chr == ':'
val[1..-1].to_sym val[1..-1].to_sym
elsif val.include? ";" elsif val.include? ';'
val.split(';') val.split(';')
elsif val == 'true' elsif val == 'true'
true true
@ -60,12 +74,12 @@ def option_maker(options, key, val)
options options
end end
# Command Line Support ############################### # Command Line Support ###############################
if ($0 == __FILE__) if $0 == __FILE__
usage = "usage: ruby #{__FILE__} (-oOptionsFile) File(s)ToMock" usage = "usage: ruby #{__FILE__} (-oOptionsFile) File(s)ToMock"
if (!ARGV[0]) unless ARGV[0]
puts usage puts usage
exit 1 exit 1
end end
@ -73,14 +87,25 @@ if ($0 == __FILE__)
options = {} options = {}
filelist = [] filelist = []
ARGV.each do |arg| ARGV.each do |arg|
if (arg =~ /^-o\"?([a-zA-Z0-9._\\\/:\s]+)\"?/) if arg =~ /^-o\"?([a-zA-Z0-9@._\\\/:\s]+)\"?/
options.merge! CMockConfig.load_config_file_from_yaml( arg.gsub(/^-o/,'') ) options.merge! CMockConfig.load_config_file_from_yaml(arg.gsub(/^-o/, ''))
elsif (arg =~ /^--([a-zA-Z0-9._\\\/:\s]+)=\"?([a-zA-Z0-9._\-\\\/:\s\;]+)\"?/) elsif arg == '--skeleton'
options = option_maker(options, $1, $2) options[:skeleton] = true
elsif arg =~ /^--strippables=\"?(.*)\"?/
# --strippables are dealt with separately since the user is allowed to
# enter any valid regular expression as argument
options = option_maker(options, 'strippables', Regexp.last_match(1))
elsif arg =~ /^--([a-zA-Z0-9._\\\/:\s]+)=\"?([a-zA-Z0-9._\-\\\/:\s\;]*)\"?/x
options = option_maker(options, Regexp.last_match(1),
Regexp.last_match(2))
else else
filelist << arg filelist << arg
end end
end end
CMock.new(options).setup_mocks(filelist) if options[:skeleton]
CMock.new(options).setup_skeletons(filelist)
else
CMock.new(options).setup_mocks(filelist)
end
end end

View File

@ -5,99 +5,120 @@
# ========================================== # ==========================================
class CMockConfig class CMockConfig
CMOCK_DEFAULT_OPTIONS =
{
:framework => :unity,
:mock_path => 'mocks',
:mock_prefix => 'Mock',
:mock_suffix => '',
:skeleton_path => '',
:weak => '',
:subdir => nil,
:plugins => [],
:strippables => ['(?:__attribute__\s*\(+.*?\)+)'],
:attributes => %w[__ramfunc __irq __fiq register extern],
:c_calling_conventions => %w[__stdcall __cdecl __fastcall],
:enforce_strict_ordering => false,
:fail_on_unexpected_calls => true,
:unity_helper_path => false,
:treat_as => {},
:treat_as_array => {},
:treat_as_void => [],
:memcmp_if_unknown => true,
:when_no_prototypes => :warn, # the options being :ignore, :warn, or :error
:when_ptr => :compare_data, # the options being :compare_ptr, :compare_data, or :smart
:verbosity => 2, # the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose
:treat_externs => :exclude, # the options being :include or :exclude
:treat_inlines => :exclude, # the options being :include or :exclude
:callback_include_count => true,
:callback_after_arg_check => false,
:includes => nil,
:includes_h_pre_orig_header => nil,
:includes_h_post_orig_header => nil,
:includes_c_pre_header => nil,
:includes_c_post_header => nil,
:orig_header_include_fmt => '#include "%s"',
:array_size_type => [],
:array_size_name => 'size|len',
:skeleton => false,
:exclude_setjmp_h => false,
CMockDefaultOptions = # Format to look for inline functions.
{ # This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static")
:framework => :unity, # There are several possibilities:
:mock_path => 'mocks', # - sometimes they appear together, sometimes individually,
:mock_prefix => 'Mock', # - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff),
:mock_suffix => '', # so we check for word boundaries when searching for them
:weak => '', # - We first remove "static inline" combinations and boil down to single inline or static statements
:subdir => nil, :inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*'] # Last part (\s*) is just to remove whitespaces (only to prettify the output)
:plugins => [], }.freeze
:strippables => ['(?:__attribute__\s*\(+.*?\)+)'],
:attributes => ['__ramfunc', '__irq', '__fiq', 'register', 'extern'],
:c_calling_conventions => ['__stdcall', '__cdecl', '__fastcall'],
:enforce_strict_ordering => false,
:fail_on_unexpected_calls => true,
:unity_helper_path => false,
:treat_as => {},
:treat_as_array => {},
:treat_as_void => [],
:memcmp_if_unknown => true,
:when_no_prototypes => :warn, #the options being :ignore, :warn, or :error
:when_ptr => :compare_data, #the options being :compare_ptr, :compare_data, or :smart
:verbosity => 2, #the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose
:treat_externs => :exclude, #the options being :include or :exclude
:callback_include_count => true,
:callback_after_arg_check => false,
:includes => nil,
:includes_h_pre_orig_header => nil,
:includes_h_post_orig_header => nil,
:includes_c_pre_header => nil,
:includes_c_post_header => nil,
:orig_header_include_fmt => "#include \"%s\"",
:array_size_type => [],
:array_size_name => 'size|len',
}
def initialize(options=nil) def initialize(options = nil)
case(options) case options
when NilClass then options = CMockDefaultOptions.clone when NilClass then options = CMOCK_DEFAULT_OPTIONS.dup
when String then options = CMockDefaultOptions.clone.merge(load_config_file_from_yaml(options)) when String then options = CMOCK_DEFAULT_OPTIONS.dup.merge(load_config_file_from_yaml(options))
when Hash then options = CMockDefaultOptions.clone.merge(options) when Hash then options = CMOCK_DEFAULT_OPTIONS.dup.merge(options)
else raise "If you specify arguments, it should be a filename or a hash of options" else raise 'If you specify arguments, it should be a filename or a hash of options'
end end
#do some quick type verification # do some quick type verification
[:plugins, :attributes, :treat_as_void].each do |opt| %i[plugins attributes treat_as_void].each do |opt|
unless (options[opt].class == Array) unless options[opt].class == Array
options[opt] = [] options[opt] = []
puts "WARNING: :#{opt.to_s} should be an array." unless (options[:verbosity] < 1) puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1
end end
end end
[:includes, :includes_h_pre_orig_header, :includes_h_post_orig_header, :includes_c_pre_header, :includes_c_post_header].each do |opt| %i[includes includes_h_pre_orig_header includes_h_post_orig_header includes_c_pre_header includes_c_post_header].each do |opt|
unless (options[opt].nil? or (options[opt].class == Array)) unless options[opt].nil? || (options[opt].class == Array)
options[opt] = [] options[opt] = []
puts "WARNING: :#{opt.to_s} should be an array." unless (options[:verbosity] < 1) puts "WARNING: :#{opt} should be an array." unless options[:verbosity] < 1
end end
end end
options[:unity_helper_path] ||= options[:unity_helper] options[:unity_helper_path] ||= options[:unity_helper]
options[:unity_helper_path] = [options[:unity_helper_path]] if options[:unity_helper_path].is_a? String options[:unity_helper_path] = [options[:unity_helper_path]] if options[:unity_helper_path].is_a? String
options[:includes_c_post_header] = ((options[:includes_c_post_header] || []) + (options[:unity_helper_path] || [])).uniq
if options[:unity_helper_path]
require 'pathname'
includes1 = options[:includes_c_post_header] || []
includes2 = options[:unity_helper_path].map do |path|
Pathname(path).relative_path_from(Pathname(options[:mock_path])).to_s
end
options[:includes_c_post_header] = (includes1 + includes2).uniq
end
options[:plugins].compact! options[:plugins].compact!
options[:plugins].map! {|p| p.to_sym} options[:plugins].map!(&:to_sym)
@options = options @options = options
treat_as_map = standard_treat_as_map()#.clone treat_as_map = standard_treat_as_map # .clone
treat_as_map.merge!(@options[:treat_as]) treat_as_map.merge!(@options[:treat_as])
@options[:treat_as] = treat_as_map @options[:treat_as] = treat_as_map
@options.each_key do |key| @options.each_key do |key|
unless methods.include?(key) unless methods.include?(key)
eval("def #{key.to_s}() return @options[:#{key.to_s}] end") eval("def #{key}() return @options[:#{key}] end")
end end
end end
end end
def load_config_file_from_yaml yaml_filename def load_config_file_from_yaml(yaml_filename)
self.class.load_config_file_from_yaml yaml_filename self.class.load_config_file_from_yaml yaml_filename
end end
def self.load_config_file_from_yaml yaml_filename def self.load_config_file_from_yaml(yaml_filename)
require 'yaml' require 'yaml'
require 'fileutils' require 'fileutils'
YAML.load_file(yaml_filename)[:cmock] YAML.load_file(yaml_filename)[:cmock]
end end
def set_path(path) def path(new_path)
@src_path = path @src_path = new_path
end end
def load_unity_helper def load_unity_helper
return nil unless (@options[:unity_helper_path]) return nil unless @options[:unity_helper_path]
return @options[:unity_helper_path].inject("") do |unity_helper, filename| @options[:unity_helper_path].inject('') do |unity_helper, filename|
unity_helper + "\n" + File.new(filename).read unity_helper + "\n" + File.new(filename).read
end end
end end

View File

@ -5,7 +5,6 @@
# ========================================== # ==========================================
class CMockFileWriter class CMockFileWriter
attr_reader :config attr_reader :config
def initialize(config) def initialize(config)
@ -13,32 +12,36 @@ class CMockFileWriter
end end
def create_subdir(subdir) def create_subdir(subdir)
if !Dir.exists?("#{@config.mock_path}/") require 'fileutils'
require 'fileutils' FileUtils.mkdir_p "#{@config.mock_path}/" unless Dir.exist?("#{@config.mock_path}/")
FileUtils.mkdir_p "#{@config.mock_path}/" FileUtils.mkdir_p "#{@config.mock_path}/#{subdir + '/' if subdir}" if subdir && !Dir.exist?("#{@config.mock_path}/#{subdir + '/' if subdir}")
end
if subdir && !Dir.exists?("#{@config.mock_path}/#{subdir+'/' if subdir}")
require 'fileutils'
FileUtils.mkdir_p "#{@config.mock_path}/#{subdir+'/' if subdir}"
end
end end
def create_file(filename, subdir) def create_file(filename, subdir)
raise "Where's the block of data to create?" unless block_given? raise "Where's the block of data to create?" unless block_given?
full_file_name_temp = "#{@config.mock_path}/#{subdir+'/' if subdir}#{filename}.new"
full_file_name_done = "#{@config.mock_path}/#{subdir+'/' if subdir}#{filename}" full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new"
full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}"
File.open(full_file_name_temp, 'w') do |file| File.open(full_file_name_temp, 'w') do |file|
yield(file, filename) yield(file, filename)
end end
update_file(full_file_name_done, full_file_name_temp) update_file(full_file_name_done, full_file_name_temp)
end end
def append_file(filename, subdir)
raise "Where's the block of data to create?" unless block_given?
full_file_name = "#{@config.skeleton_path}/#{subdir + '/' if subdir}#{filename}"
File.open(full_file_name, 'a') do |file|
yield(file, filename)
end
end
private ################################### private ###################################
def update_file(dest, src) def update_file(dest, src)
require 'fileutils' require 'fileutils'
FileUtils.rm(dest) if (File.exist?(dest)) FileUtils.rm(dest, :force => true)
FileUtils.cp(src, dest) FileUtils.mv(src, dest)
FileUtils.rm(src)
end end
end end

View File

@ -5,8 +5,7 @@
# ========================================== # ==========================================
class CMockGenerator class CMockGenerator
attr_accessor :config, :file_writer, :module_name, :module_ext, :clean_mock_name, :mock_name, :utils, :plugins, :weak, :ordered
attr_accessor :config, :file_writer, :module_name, :clean_mock_name, :mock_name, :utils, :plugins, :weak, :ordered
def initialize(config, file_writer, utils, plugins) def initialize(config, file_writer, utils, plugins)
@file_writer = file_writer @file_writer = file_writer
@ -16,89 +15,147 @@ class CMockGenerator
@prefix = @config.mock_prefix @prefix = @config.mock_prefix
@suffix = @config.mock_suffix @suffix = @config.mock_suffix
@weak = @config.weak @weak = @config.weak
@ordered = @config.enforce_strict_ordering @include_inline = @config.treat_inlines
@framework = @config.framework.to_s @ordered = @config.enforce_strict_ordering
@framework = @config.framework.to_s
@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
@subdir = @config.subdir
@subdir = @config.subdir @includes_h_pre_orig_header = (@config.includes || @config.includes_h_pre_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
@includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
@includes_h_pre_orig_header = (@config.includes || @config.includes_h_pre_orig_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} @includes_c_pre_header = (@config.includes_c_pre_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
@includes_h_post_orig_header = (@config.includes_h_post_orig_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""} @includes_c_post_header = (@config.includes_c_post_header || []).map { |h| h =~ /</ ? h : "\"#{h}\"" }
@includes_c_pre_header = (@config.includes_c_pre_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""}
@includes_c_post_header = (@config.includes_c_post_header || []).map{|h| h =~ /</ ? h : "\"#{h}\""}
here = File.dirname __FILE__ here = File.dirname __FILE__
unity_path_in_ceedling = "#{here}/../../unity" # path to Unity from within Ceedling unity_path_in_ceedling = "#{here}/../../unity" # path to Unity from within Ceedling
unity_path_in_cmock = "#{here}/../vendor/unity" # path to Unity from within CMock unity_path_in_cmock = "#{here}/../vendor/unity" # path to Unity from within CMock
# path to Unity as specified by env var # path to Unity as specified by env var
unity_path_in_env = ENV.has_key?("UNITY_DIR") ? File.expand_path(ENV.fetch("UNITY_DIR")) : nil unity_path_in_env = ENV.key?('UNITY_DIR') ? File.expand_path(ENV.fetch('UNITY_DIR')) : nil
if unity_path_in_env and File.exist? unity_path_in_env if unity_path_in_env && File.exist?(unity_path_in_env)
require "#{unity_path_in_env}/auto/type_sanitizer" require "#{unity_path_in_env}/auto/type_sanitizer"
elsif File.exist? unity_path_in_ceedling elsif File.exist? unity_path_in_ceedling
require "#{unity_path_in_ceedling}/auto/type_sanitizer" require "#{unity_path_in_ceedling}/auto/type_sanitizer"
elsif File.exist? unity_path_in_cmock elsif File.exist? unity_path_in_cmock
require "#{unity_path_in_cmock}/auto/type_sanitizer" require "#{unity_path_in_cmock}/auto/type_sanitizer"
else else
raise "Failed to find an instance of Unity to pull in type_sanitizer module!" raise 'Failed to find an instance of Unity to pull in type_sanitizer module!'
end end
end end
def create_mock(module_name, parsed_stuff) def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil)
@module_name = module_name # determine the name for our new mock
@mock_name = @prefix + @module_name + @suffix mock_name = @prefix + module_name + @suffix
@clean_mock_name = TypeSanitizer.sanitize_c_identifier(@mock_name)
create_mock_subdir() # determine the folder our mock will reside
create_mock_header_file(parsed_stuff) mock_folder = if folder && @subdir
create_mock_source_file(parsed_stuff) File.join(@subdir, folder)
elsif @subdir
@subdir
else
folder
end
# adds a trailing slash to the folder output
mock_folder = File.join(mock_folder, '') if mock_folder
# create out mock project from incoming data
mock_project = {
:module_name => module_name,
:module_ext => (module_ext || '.h'),
:mock_name => mock_name,
:clean_name => TypeSanitizer.sanitize_c_identifier(mock_name),
:folder => mock_folder,
:parsed_stuff => parsed_stuff,
:skeleton => false
}
create_mock_subdir(mock_project)
create_mock_header_file(mock_project)
create_mock_source_file(mock_project)
end
def create_skeleton(module_name, parsed_stuff)
mock_project = {
:module_name => module_name,
:module_ext => '.h',
:parsed_stuff => parsed_stuff,
:skeleton => true
}
create_skeleton_source_file(mock_project)
end end
private if $ThisIsOnlyATest.nil? ############################## private if $ThisIsOnlyATest.nil? ##############################
def create_mock_subdir() def create_mock_subdir(mock_project)
@file_writer.create_subdir(@subdir) @file_writer.create_subdir(mock_project[:folder])
end end
def create_mock_header_file(parsed_stuff) def create_using_statement(file, function)
@file_writer.create_file(@mock_name + ".h", @subdir) do |file, filename| file << "using namespace #{function[:namespace].join('::')};\n" unless function[:namespace].empty?
create_mock_header_header(file, filename) end
create_mock_header_service_call_declarations(file)
create_typedefs(file, parsed_stuff[:typedefs]) def create_mock_header_file(mock_project)
parsed_stuff[:functions].each do |function| if @include_inline == :include
@file_writer.create_file(mock_project[:module_name] + (mock_project[:module_ext]), mock_project[:folder]) do |file, _filename|
file << mock_project[:parsed_stuff][:normalized_source]
end
end
@file_writer.create_file(mock_project[:mock_name] + mock_project[:module_ext], mock_project[:folder]) do |file, filename|
create_mock_header_header(file, filename, mock_project)
create_mock_header_service_call_declarations(file, mock_project)
create_typedefs(file, mock_project)
mock_project[:parsed_stuff][:functions].each do |function|
create_using_statement(file, function)
file << @plugins.run(:mock_function_declarations, function) file << @plugins.run(:mock_function_declarations, function)
end end
create_mock_header_footer(file) create_mock_header_footer(file)
end end
end end
def create_mock_source_file(parsed_stuff) def create_mock_source_file(mock_project)
@file_writer.create_file(@mock_name + ".c", @subdir) do |file, filename| @file_writer.create_file(mock_project[:mock_name] + '.c', mock_project[:folder]) do |file, filename|
create_source_header_section(file, filename, parsed_stuff[:functions]) create_source_header_section(file, filename, mock_project)
create_instance_structure(file, parsed_stuff[:functions]) create_instance_structure(file, mock_project)
create_extern_declarations(file) create_extern_declarations(file)
create_mock_verify_function(file, parsed_stuff[:functions]) create_mock_verify_function(file, mock_project)
create_mock_init_function(file) create_mock_init_function(file, mock_project)
create_mock_destroy_function(file, parsed_stuff[:functions]) create_mock_destroy_function(file, mock_project)
parsed_stuff[:functions].each do |function| mock_project[:parsed_stuff][:functions].each do |function|
create_mock_implementation(file, function) create_mock_implementation(file, function)
create_mock_interfaces(file, function) create_mock_interfaces(file, function)
end end
end end
end end
def create_mock_header_header(file, filename) def create_skeleton_source_file(mock_project)
define_name = @clean_mock_name.upcase filename = "#{@config.mock_path}/#{@subdir + '/' if @subdir}#{mock_project[:module_name]}.c"
orig_filename = (@subdir ? @subdir + "/" : "") + @module_name + ".h" existing = File.exist?(filename) ? File.read(filename) : ''
@file_writer.append_file(mock_project[:module_name] + '.c', @subdir) do |file, fullname|
blank_project = mock_project.clone
blank_project[:parsed_stuff] = { :functions => [] }
create_source_header_section(file, fullname, blank_project) if existing.empty?
mock_project[:parsed_stuff][:functions].each do |function|
create_function_skeleton(file, function, existing)
end
end
end
def create_mock_header_header(file, _filename, mock_project)
define_name = mock_project[:clean_name].upcase
orig_filename = (mock_project[:folder] || '') + mock_project[:module_name] + mock_project[:module_ext]
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n"
file << "#ifndef _#{define_name}_H\n" file << "#ifndef _#{define_name}_H\n"
file << "#define _#{define_name}_H\n\n" file << "#define _#{define_name}_H\n\n"
file << "#include \"#{@framework}.h\"\n" file << "#include \"#{@framework}.h\"\n"
@includes_h_pre_orig_header.each {|inc| file << "#include #{inc}\n"} @includes_h_pre_orig_header.each { |inc| file << "#include #{inc}\n" }
file << @config.orig_header_include_fmt.gsub(/%s/, "#{orig_filename}") + "\n" file << @config.orig_header_include_fmt.gsub(/%s/, orig_filename.to_s) + "\n"
@includes_h_post_orig_header.each {|inc| file << "#include #{inc}\n"} @includes_h_post_orig_header.each { |inc| file << "#include #{inc}\n" }
plugin_includes = @plugins.run(:include_files) plugin_includes = @plugins.run(:include_files)
file << plugin_includes if (!plugin_includes.empty?) file << plugin_includes unless plugin_includes.empty?
file << "\n" file << "\n"
file << "/* Ignore the following warnings, since we are copying code */\n" file << "/* Ignore the following warnings, since we are copying code */\n"
file << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" file << "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n"
@ -114,16 +171,16 @@ class CMockGenerator
file << "\n" file << "\n"
end end
def create_typedefs(file, typedefs) def create_typedefs(file, mock_project)
file << "\n" file << "\n"
typedefs.each {|typedef| file << "#{typedef}\n" } mock_project[:parsed_stuff][:typedefs].each { |typedef| file << "#{typedef}\n" }
file << "\n\n" file << "\n\n"
end end
def create_mock_header_service_call_declarations(file) def create_mock_header_service_call_declarations(file, mock_project)
file << "void #{@clean_mock_name}_Init(void);\n" file << "void #{mock_project[:clean_name]}_Init(void);\n"
file << "void #{@clean_mock_name}_Destroy(void);\n" file << "void #{mock_project[:clean_name]}_Destroy(void);\n"
file << "void #{@clean_mock_name}_Verify(void);\n\n" file << "void #{mock_project[:clean_name]}_Verify(void);\n\n"
end end
def create_mock_header_footer(header) def create_mock_header_footer(header)
@ -137,21 +194,23 @@ class CMockGenerator
header << "#endif\n" header << "#endif\n"
end end
def create_source_header_section(file, filename, functions) def create_source_header_section(file, filename, mock_project)
header_file = (@subdir ? @subdir + '/' : '') + filename.gsub(".c",".h") header_file = (mock_project[:folder] || '') + filename.gsub('.c', mock_project[:module_ext])
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty?
file << "#include <string.h>\n" file << "#include <string.h>\n"
file << "#include <stdlib.h>\n" file << "#include <stdlib.h>\n"
file << "#include <setjmp.h>\n" unless @exclude_setjmp_h
file << "#include <setjmp.h>\n"
end
file << "#include \"cmock.h\"\n" file << "#include \"cmock.h\"\n"
@includes_c_pre_header.each {|inc| file << "#include #{inc}\n"} @includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
file << "#include \"#{header_file}\"\n" file << "#include \"#{header_file}\"\n"
@includes_c_post_header.each {|inc| file << "#include #{inc}\n"} @includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
file << "\n" file << "\n"
strs = [] strs = []
functions.each do |func| mock_project[:parsed_stuff][:functions].each do |func|
strs << func[:name] strs << func[:name]
func[:args].each {|arg| strs << arg[:name] } func[:args].each { |arg| strs << arg[:name] }
end end
strs.uniq.sort.each do |str| strs.uniq.sort.each do |str|
file << "static const char* CMockString_#{str} = \"#{str}\";\n" file << "static const char* CMockString_#{str} = \"#{str}\";\n"
@ -159,15 +218,16 @@ class CMockGenerator
file << "\n" file << "\n"
end end
def create_instance_structure(file, functions) def create_instance_structure(file, mock_project)
functions = mock_project[:parsed_stuff][:functions]
functions.each do |function| functions.each do |function|
file << "typedef struct _CMOCK_#{function[:name]}_CALL_INSTANCE\n{\n" file << "typedef struct _CMOCK_#{function[:name]}_CALL_INSTANCE\n{\n"
file << " UNITY_LINE_TYPE LineNumber;\n" file << " UNITY_LINE_TYPE LineNumber;\n"
file << @plugins.run(:instance_typedefs, function) file << @plugins.run(:instance_typedefs, function)
file << "\n} CMOCK_#{function[:name]}_CALL_INSTANCE;\n\n" file << "\n} CMOCK_#{function[:name]}_CALL_INSTANCE;\n\n"
end end
file << "static struct #{@clean_mock_name}Instance\n{\n" file << "static struct #{mock_project[:clean_name]}Instance\n{\n"
if (functions.size == 0) if functions.empty?
file << " unsigned char placeHolder;\n" file << " unsigned char placeHolder;\n"
end end
functions.each do |function| functions.each do |function|
@ -178,17 +238,19 @@ class CMockGenerator
end end
def create_extern_declarations(file) def create_extern_declarations(file)
file << "extern jmp_buf AbortFrame;\n" unless @exclude_setjmp_h
if (@ordered) file << "extern jmp_buf AbortFrame;\n"
end
if @ordered
file << "extern int GlobalExpectCount;\n" file << "extern int GlobalExpectCount;\n"
file << "extern int GlobalVerifyOrder;\n" file << "extern int GlobalVerifyOrder;\n"
end end
file << "\n" file << "\n"
end end
def create_mock_verify_function(file, functions) def create_mock_verify_function(file, mock_project)
file << "void #{@clean_mock_name}_Verify(void)\n{\n" file << "void #{mock_project[:clean_name]}_Verify(void)\n{\n"
verifications = functions.collect do |function| verifications = mock_project[:parsed_stuff][:functions].collect do |function|
v = @plugins.run(:mock_verify, function) v = @plugins.run(:mock_verify, function)
v.empty? ? v : [" call_instance = Mock.#{function[:name]}_CallInstance;\n", v] v.empty? ? v : [" call_instance = Mock.#{function[:name]}_CallInstance;\n", v]
end.join end.join
@ -200,23 +262,23 @@ class CMockGenerator
file << "}\n\n" file << "}\n\n"
end end
def create_mock_init_function(file) def create_mock_init_function(file, mock_project)
file << "void #{@clean_mock_name}_Init(void)\n{\n" file << "void #{mock_project[:clean_name]}_Init(void)\n{\n"
file << " #{@clean_mock_name}_Destroy();\n" file << " #{mock_project[:clean_name]}_Destroy();\n"
file << "}\n\n" file << "}\n\n"
end end
def create_mock_destroy_function(file, functions) def create_mock_destroy_function(file, mock_project)
file << "void #{@clean_mock_name}_Destroy(void)\n{\n" file << "void #{mock_project[:clean_name]}_Destroy(void)\n{\n"
file << " CMock_Guts_MemFreeAll();\n" file << " CMock_Guts_MemFreeAll();\n"
file << " memset(&Mock, 0, sizeof(Mock));\n" file << " memset(&Mock, 0, sizeof(Mock));\n"
file << functions.collect {|function| @plugins.run(:mock_destroy, function)}.join file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_destroy, function) }.join
unless (@fail_on_unexpected_calls) unless @fail_on_unexpected_calls
file << functions.collect {|function| @plugins.run(:mock_ignore, function)}.join file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_ignore, function) }.join
end end
if (@ordered) if @ordered
file << " GlobalExpectCount = 0;\n" file << " GlobalExpectCount = 0;\n"
file << " GlobalVerifyOrder = 0;\n" file << " GlobalVerifyOrder = 0;\n"
end end
@ -229,17 +291,28 @@ class CMockGenerator
(function[:return][:type]) + (function[:return][:type]) +
(function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '') (function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '')
args_string = function[:args_string] args_string = function[:args_string]
args_string += (", " + function[:var_arg]) unless (function[:var_arg].nil?) args_string += (', ' + function[:var_arg]) unless function[:var_arg].nil?
# Encapsulate in namespace(s) if applicable
function[:namespace].each do |ns|
file << "namespace #{ns} {\n"
end
# Determine class prefix (if any)
cls_pre = ''
unless function[:class].nil?
cls_pre = "#{function[:class]}::"
end
# Create mock function # Create mock function
if (not @weak.empty?) unless @weak.empty?
file << "#if defined (__IAR_SYSTEMS_ICC__)\n" file << "#if defined (__IAR_SYSTEMS_ICC__)\n"
file << "#pragma weak #{function[:name]}\n" file << "#pragma weak #{function[:unscoped_name]}\n"
file << "#else\n" file << "#else\n"
file << "#{function_mod_and_rettype} #{function[:name]}(#{args_string}) #{weak};\n" file << "#{function_mod_and_rettype} #{function[:unscoped_name]}(#{args_string}) #{weak};\n"
file << "#endif\n\n" file << "#endif\n\n"
end end
file << "#{function_mod_and_rettype} #{function[:name]}(#{args_string})\n" file << "#{function_mod_and_rettype} #{cls_pre}#{function[:unscoped_name]}(#{args_string})\n"
file << "{\n" file << "{\n"
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"
@ -249,7 +322,7 @@ class CMockGenerator
file << @plugins.run(:mock_implementation_precheck, function) file << @plugins.run(:mock_implementation_precheck, function)
file << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore);\n" file << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore);\n"
file << " cmock_line = cmock_call_instance->LineNumber;\n" file << " cmock_line = cmock_call_instance->LineNumber;\n"
if (@ordered) if @ordered
file << " if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder)\n" file << " if (cmock_call_instance->CallOrder > ++GlobalVerifyOrder)\n"
file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly);\n" file << " UNITY_TEST_FAIL(cmock_line, CMockStringCalledEarly);\n"
file << " if (cmock_call_instance->CallOrder < GlobalVerifyOrder)\n" file << " if (cmock_call_instance->CallOrder < GlobalVerifyOrder)\n"
@ -257,12 +330,39 @@ class CMockGenerator
end end
file << @plugins.run(:mock_implementation, function) file << @plugins.run(:mock_implementation, function)
file << " UNITY_CLR_DETAILS();\n" file << " UNITY_CLR_DETAILS();\n"
file << " return cmock_call_instance->ReturnVal;\n" unless (function[:return][:void?]) file << " return cmock_call_instance->ReturnVal;\n" unless function[:return][:void?]
file << "}\n\n" file << "}\n"
# Close any namespace(s) opened above
function[:namespace].each do
file << "}\n"
end
file << "\n"
end end
def create_mock_interfaces(file, function) def create_mock_interfaces(file, function)
file << @utils.code_add_argument_loader(function) file << @utils.code_add_argument_loader(function)
file << @plugins.run(:mock_interfaces, function) file << @plugins.run(:mock_interfaces, function)
end end
def create_function_skeleton(file, function, existing)
# prepare return value and arguments
function_mod_and_rettype = (function[:modifier].empty? ? '' : "#{function[:modifier]} ") +
(function[:return][:type]) +
(function[:c_calling_convention] ? " #{function[:c_calling_convention]}" : '')
args_string = function[:args_string]
args_string += (', ' + function[:var_arg]) unless function[:var_arg].nil?
decl = "#{function_mod_and_rettype} #{function[:name]}(#{args_string})"
return if existing.include?(decl)
file << "#{decl}\n"
file << "{\n"
file << " /*TODO: Implement Me!*/\n"
function[:args].each { |arg| file << " (void)#{arg[:name]};\n" }
file << " return (#{(function[:return][:type])})0;\n" unless function[:return][:void?]
file << "}\n\n"
end
end end

View File

@ -5,7 +5,6 @@
# ========================================== # ==========================================
class CMockGeneratorPluginArray class CMockGeneratorPluginArray
attr_reader :priority attr_reader :priority
attr_accessor :config, :utils, :unity_helper, :ordered attr_accessor :config, :utils, :unity_helper, :ordered
def initialize(config, utils) def initialize(config, utils)
@ -18,46 +17,47 @@ class CMockGeneratorPluginArray
end end
def instance_typedefs(function) def instance_typedefs(function)
function[:args].inject("") do |all, arg| function[:args].inject('') do |all, arg|
(arg[:ptr?]) ? all + " int Expected_#{arg[:name]}_Depth;\n" : all arg[:ptr?] ? all + " int Expected_#{arg[:name]}_Depth;\n" : all
end end
end end
def mock_function_declarations(function) def mock_function_declarations(function)
return nil unless function[:contains_ptr?] return nil unless function[:contains_ptr?]
args_call = function[:args].map{|m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : "#{m[:name]}"}.join(', ')
args_call = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : (m[:name]).to_s }.join(', ')
args_string = function[:args].map do |m| args_string = function[:args].map do |m|
type = @utils.arg_type_with_const(m) type = @utils.arg_type_with_const(m)
m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}"
end.join(', ') end.join(', ')
if (function[:return][:void?]) if function[:return][:void?]
return "#define #{function[:name]}_ExpectWithArray(#{args_call}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call})\n" + return "#define #{function[:name]}_ExpectWithArray(#{args_call}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call})\n" \
"void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string});\n" "void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string});\n"
else else
return "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call}, cmock_retval)\n" + return "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call}, cmock_retval)\n" \
"void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]});\n" "void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]});\n"
end end
end end
def mock_interfaces(function) def mock_interfaces(function)
return nil unless function[:contains_ptr?] return nil unless function[:contains_ptr?]
lines = [] lines = []
func_name = function[:name] func_name = function[:name]
args_string = function[:args].map do |m| args_string = function[:args].map do |m|
type = @utils.arg_type_with_const(m) type = @utils.arg_type_with_const(m)
m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}"
end.join(', ') end.join(', ')
call_string = function[:args].map{|m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : m[:name]}.join(', ') call_string = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : m[:name] }.join(', ')
if (function[:return][:void?]) lines << if function[:return][:void?]
lines << "void #{func_name}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string})\n" "void #{func_name}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string})\n"
else else
lines << "void #{func_name}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]})\n" "void #{func_name}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]})\n"
end end
lines << "{\n" lines << "{\n"
lines << @utils.code_add_base_expectation(func_name) lines << @utils.code_add_base_expectation(func_name)
lines << " CMockExpectParameters_#{func_name}(cmock_call_instance, #{call_string});\n" lines << " CMockExpectParameters_#{func_name}(cmock_call_instance, #{call_string});\n"
lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" unless (function[:return][:void?]) lines << " cmock_call_instance->ReturnVal = cmock_to_return;\n" unless function[:return][:void?]
lines << "}\n\n" lines << "}\n\n"
end end
end end

View File

@ -5,7 +5,6 @@
# ========================================== # ==========================================
class CMockGeneratorPluginCallback class CMockGeneratorPluginCallback
attr_accessor :include_count attr_accessor :include_count
attr_reader :priority attr_reader :priority
attr_reader :config, :utils attr_reader :config, :utils
@ -20,7 +19,7 @@ class CMockGeneratorPluginCallback
def instance_structure(function) def instance_structure(function)
func_name = function[:name] func_name = function[:name]
" int #{func_name}_CallbackBool;\n" \ " char #{func_name}_CallbackBool;\n" \
" CMOCK_#{func_name}_CALLBACK #{func_name}_CallbackFunctionPointer;\n" \ " CMOCK_#{func_name}_CALLBACK #{func_name}_CallbackFunctionPointer;\n" \
" int #{func_name}_CallbackCalls;\n" " int #{func_name}_CallbackCalls;\n"
end end
@ -30,7 +29,7 @@ class CMockGeneratorPluginCallback
return_type = function[:return][:type] return_type = function[:return][:type]
action = @config.callback_after_arg_check ? 'AddCallback' : 'Stub' action = @config.callback_after_arg_check ? 'AddCallback' : 'Stub'
style = (@include_count ? 1 : 0) | (function[:args].empty? ? 0 : 2) style = (@include_count ? 1 : 0) | (function[:args].empty? ? 0 : 2)
styles = [ "void", "int cmock_num_calls", function[:args_string], "#{function[:args_string]}, int cmock_num_calls" ] styles = ['void', 'int cmock_num_calls', function[:args_string], "#{function[:args_string]}, int cmock_num_calls"]
"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" \
@ -60,29 +59,30 @@ class CMockGeneratorPluginCallback
" UNITY_CLR_DETAILS();\n" \ " UNITY_CLR_DETAILS();\n" \
" return;\n }\n" " return;\n }\n"
else else
" #{function[:return][:type]} ret = #{generate_call(function)};\n" \ " #{function[:return][:type]} cmock_cb_ret = #{generate_call(function)};\n" \
" UNITY_CLR_DETAILS();\n" \ " UNITY_CLR_DETAILS();\n" \
" return ret;\n }\n" " return cmock_cb_ret;\n }\n"
end end
end end
def mock_interfaces(function) def mock_interfaces(function)
func_name = function[:name] func_name = function[:name]
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 << " Mock.#{func_name}_IgnoreBool = (int)0;\n" if has_ignore lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore
lines << " Mock.#{func_name}_CallbackBool = (int)1;\n" lines << " Mock.#{func_name}_CallbackBool = (char)1;\n"
lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\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 << " Mock.#{func_name}_IgnoreBool = (int)0;\n" if has_ignore lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if has_ignore
lines << " Mock.#{func_name}_CallbackBool = (int)0;\n" lines << " Mock.#{func_name}_CallbackBool = (char)0;\n"
lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n" lines << " Mock.#{func_name}_CallbackFunctionPointer = Callback;\n}\n\n"
end end
def mock_verify(function) def mock_verify(function)
func_name = function[:name] func_name = function[:name]
" if (Mock.#{func_name}_CallbackFunctionPointer != NULL)\n call_instance = CMOCK_GUTS_NONE;\n" " if (Mock.#{func_name}_CallbackFunctionPointer != NULL)\n {\n" \
" call_instance = CMOCK_GUTS_NONE;\n" \
" (void)call_instance;\n }\n"
end end
end end

View File

@ -5,7 +5,6 @@
# ========================================== # ==========================================
class CMockGeneratorPluginCexception class CMockGeneratorPluginCexception
attr_reader :priority attr_reader :priority
attr_reader :config, :utils attr_reader :config, :utils
@ -13,39 +12,39 @@ class CMockGeneratorPluginCexception
@config = config @config = config
@utils = utils @utils = utils
@priority = 7 @priority = 7
raise 'Error: cexception is not supported without setjmp support' if @config.exclude_setjmp_h
end end
def include_files def include_files
return "#include \"CException.h\"\n" "#include \"CException.h\"\n"
end end
def instance_typedefs(function) def instance_typedefs(_function)
" CEXCEPTION_T ExceptionToThrow;\n" " CEXCEPTION_T ExceptionToThrow;\n"
end end
def mock_function_declarations(function) def mock_function_declarations(function)
if (function[:args_string] == "void") if function[:args_string] == 'void'
return "#define #{function[:name]}_ExpectAndThrow(cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, cmock_to_throw)\n" + "#define #{function[:name]}_ExpectAndThrow(cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, cmock_to_throw)\n" \
"void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, CEXCEPTION_T cmock_to_throw);\n" "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, CEXCEPTION_T cmock_to_throw);\n"
else else
return "#define #{function[:name]}_ExpectAndThrow(#{function[:args_call]}, cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, #{function[:args_call]}, cmock_to_throw)\n" + "#define #{function[:name]}_ExpectAndThrow(#{function[:args_call]}, cmock_to_throw) #{function[:name]}_CMockExpectAndThrow(__LINE__, #{function[:args_call]}, cmock_to_throw)\n" \
"void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, CEXCEPTION_T cmock_to_throw);\n" "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, CEXCEPTION_T cmock_to_throw);\n"
end end
end end
def mock_implementation(function) def mock_implementation(_function)
" if (cmock_call_instance->ExceptionToThrow != CEXCEPTION_NONE)\n {\n" + " if (cmock_call_instance->ExceptionToThrow != CEXCEPTION_NONE)\n {\n" \
" UNITY_CLR_DETAILS();\n" + " UNITY_CLR_DETAILS();\n" \
" Throw(cmock_call_instance->ExceptionToThrow);\n }\n" " Throw(cmock_call_instance->ExceptionToThrow);\n }\n"
end end
def mock_interfaces(function) def mock_interfaces(function)
arg_insert = (function[:args_string] == "void") ? "" : "#{function[:args_string]}, " arg_insert = function[:args_string] == 'void' ? '' : "#{function[:args_string]}, "
[ "void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{arg_insert}CEXCEPTION_T cmock_to_throw)\n{\n", ["void #{function[:name]}_CMockExpectAndThrow(UNITY_LINE_TYPE cmock_line, #{arg_insert}CEXCEPTION_T cmock_to_throw)\n{\n",
@utils.code_add_base_expectation(function[:name]), @utils.code_add_base_expectation(function[:name]),
@utils.code_call_argument_loader(function), @utils.code_call_argument_loader(function),
" cmock_call_instance->ExceptionToThrow = cmock_to_throw;\n", " cmock_call_instance->ExceptionToThrow = cmock_to_throw;\n",
"}\n\n" ].join "}\n\n"].join
end end
end end

View File

@ -5,7 +5,6 @@
# ========================================== # ==========================================
class CMockGeneratorPluginExpect class CMockGeneratorPluginExpect
attr_reader :priority attr_reader :priority
attr_accessor :config, :utils, :unity_helper, :ordered attr_accessor :config, :utils, :unity_helper, :ordered
@ -17,7 +16,7 @@ class CMockGeneratorPluginExpect
@unity_helper = @utils.helpers[:unity_helper] @unity_helper = @utils.helpers[:unity_helper]
@priority = 5 @priority = 5
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
else else
alias :mock_implementation :mock_implementation_always_check_args alias :mock_implementation :mock_implementation_always_check_args
@ -25,9 +24,9 @@ class CMockGeneratorPluginExpect
end end
def instance_typedefs(function) def instance_typedefs(function)
lines = "" lines = ''
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[:type]} Expected_#{arg[:name]};\n"
end end
@ -35,27 +34,25 @@ class CMockGeneratorPluginExpect
end end
def mock_function_declarations(function) def mock_function_declarations(function)
if (function[:args].empty?) if function[:args].empty?
if (function[:return][:void?]) if function[:return][:void?]
return "#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n" + "#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n" \
"void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n" "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n"
else else
return "#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n" + "#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n" \
"void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
end end
elsif function[:return][:void?]
"#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n" \
"void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n"
else else
if (function[:return][:void?]) "#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n" \
return "#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n" + "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n"
"void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n"
else
return "#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n" +
"void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n"
end
end end
end end
def mock_implementation_always_check_args(function) def mock_implementation_always_check_args(function)
lines = "" lines = ''
function[:args].each do |arg| function[:args].each do |arg|
lines << @utils.code_verify_an_arg_expectation(function, arg) lines << @utils.code_verify_an_arg_expectation(function, arg)
end end
@ -63,7 +60,8 @@ class CMockGeneratorPluginExpect
end end
def mock_implementation_might_check_args(function) def mock_implementation_might_check_args(function)
return "" if (function[:args].empty?) return '' if function[:args].empty?
lines = " if (!cmock_call_instance->ExpectAnyArgsBool)\n {\n" lines = " if (!cmock_call_instance->ExpectAnyArgsBool)\n {\n"
function[:args].each do |arg| function[:args].each do |arg|
lines << @utils.code_verify_an_arg_expectation(function, arg) lines << @utils.code_verify_an_arg_expectation(function, arg)
@ -73,31 +71,30 @@ class CMockGeneratorPluginExpect
end end
def mock_interfaces(function) def mock_interfaces(function)
lines = "" lines = ''
func_name = function[:name] func_name = function[:name]
if (function[:return][:void?]) lines << if function[:return][:void?]
if (function[:args_string] == "void") if function[:args_string] == 'void'
lines << "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line)\n{\n" "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line)\n{\n"
else else
lines << "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]})\n{\n" "void #{func_name}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]})\n{\n"
end end
else elsif function[:args_string] == 'void'
if (function[:args_string] == "void") "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n"
lines << "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]})\n{\n" else
else "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n"
lines << "void #{func_name}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]})\n{\n" end
end
end
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?]
lines << "}\n\n" lines << "}\n\n"
end end
def mock_verify(function) def mock_verify(function)
" UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" + " if (CMOCK_GUTS_NONE != call_instance)\n" \
" UNITY_TEST_ASSERT(CMOCK_GUTS_NONE == call_instance, cmock_line, CMockStringCalledLess);\n" + " {\n" \
" UNITY_CLR_DETAILS();\n" " UNITY_SET_DETAIL(CMockString_#{function[:name]});\n" \
" UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess);\n" \
" }\n"
end end
end end

View File

@ -5,7 +5,6 @@
# ========================================== # ==========================================
class CMockGeneratorPluginExpectAnyArgs class CMockGeneratorPluginExpectAnyArgs
attr_reader :priority attr_reader :priority
attr_reader :config, :utils attr_reader :config, :utils
@ -15,39 +14,37 @@ class CMockGeneratorPluginExpectAnyArgs
@priority = 3 @priority = 3
end end
def instance_typedefs(function) def instance_typedefs(_function)
" int ExpectAnyArgsBool;\n" " char ExpectAnyArgsBool;\n"
end end
def mock_function_declarations(function) def mock_function_declarations(function)
unless (function[:args].empty?) if function[:args].empty?
if (function[:return][:void?]) ''
return "#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" + elsif function[:return][:void?]
"void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n" "#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" \
else "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n"
return "#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n" +
"void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
end
else else
"" "#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n" \
"void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
end end
end end
def mock_interfaces(function) def mock_interfaces(function)
lines = "" lines = ''
unless (function[:args].empty?) unless function[:args].empty?
if (function[:return][:void?]) lines << if function[:return][:void?]
lines << "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line)\n{\n" "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line)\n{\n"
else else
lines << "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 << @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"
end end
lines << " cmock_call_instance->ExpectAnyArgsBool = (int)1;\n" lines << " cmock_call_instance->ExpectAnyArgsBool = (char)1;\n"
lines << "}\n\n" lines << "}\n\n"
end end
return lines lines
end end
end end

View File

@ -5,7 +5,6 @@
# ========================================== # ==========================================
class CMockGeneratorPluginIgnore class CMockGeneratorPluginIgnore
attr_reader :priority attr_reader :priority
attr_reader :config, :utils attr_reader :config, :utils
@ -16,56 +15,70 @@ class CMockGeneratorPluginIgnore
end end
def instance_structure(function) def instance_structure(function)
if (function[:return][:void?]) if function[:return][:void?]
" int #{function[:name]}_IgnoreBool;\n" " char #{function[:name]}_IgnoreBool;\n"
else else
" int #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n" " char #{function[:name]}_IgnoreBool;\n #{function[:return][:type]} #{function[:name]}_FinalReturn;\n"
end end
end end
def mock_function_declarations(function) def mock_function_declarations(function)
if (function[:return][:void?]) lines = if function[:return][:void?]
return "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" + "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \
"void #{function[:name]}_CMockIgnore(void);\n" "void #{function[:name]}_CMockIgnore(void);\n"
else else
return "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n" + "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n" \
"void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n" "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
end end
# Add stop ignore function. it does not matter if there are any args
lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \
"void #{function[:name]}_CMockStopIgnore(void);\n"
lines
end end
def mock_implementation_precheck(function) def mock_implementation_precheck(function)
lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n" lines = " if (Mock.#{function[:name]}_IgnoreBool)\n {\n"
lines << " UNITY_CLR_DETAILS();\n" lines << " UNITY_CLR_DETAILS();\n"
if (function[:return][:void?]) if function[:return][:void?]
lines << " return;\n }\n" lines << " return;\n }\n"
else else
retval = function[:return].merge( { :name => "cmock_call_instance->ReturnVal"} ) retval = function[:return].merge(:name => 'cmock_call_instance->ReturnVal')
lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n" lines << " if (cmock_call_instance == NULL)\n return Mock.#{function[:name]}_FinalReturn;\n"
lines << " " + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless (retval[:void?]) lines << ' ' + @utils.code_assign_argument_quickly("Mock.#{function[:name]}_FinalReturn", retval) unless retval[:void?]
lines << " return cmock_call_instance->ReturnVal;\n }\n" lines << " return cmock_call_instance->ReturnVal;\n }\n"
end end
lines lines
end end
def mock_interfaces(function) def mock_interfaces(function)
lines = "" lines = ''
if (function[:return][:void?]) lines << if function[:return][:void?]
lines << "void #{function[:name]}_CMockIgnore(void)\n{\n" "void #{function[:name]}_CMockIgnore(void)\n{\n"
else else
lines << "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
if (!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
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"
end end
lines << " Mock.#{function[:name]}_IgnoreBool = (int)1;\n" lines << " Mock.#{function[:name]}_IgnoreBool = (char)1;\n"
lines << "}\n\n"
# Add stop ignore function. it does not matter if there are any args
lines << "void #{function[:name]}_CMockStopIgnore(void)\n{\n"
unless function[:return][:void?]
lines << " if(Mock.#{function[:name]}_IgnoreBool)\n"
lines << " Mock.#{function[:name]}_CallInstance = CMock_Guts_MemNext(Mock.#{function[:name]}_CallInstance);\n"
end
lines << " Mock.#{function[:name]}_IgnoreBool = (char)0;\n"
lines << "}\n\n" lines << "}\n\n"
end end
def mock_ignore(function) def mock_ignore(function)
" Mock.#{function[:name]}_IgnoreBool = (int) 1;\n" " Mock.#{function[:name]}_IgnoreBool = (char) 1;\n"
end end
def mock_verify(function) def mock_verify(function)

View File

@ -2,21 +2,21 @@ 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
end end
def instance_typedefs(function) def instance_typedefs(function)
lines = "" lines = ''
function[:args].each do |arg| function[:args].each do |arg|
lines << " int IgnoreArg_#{arg[:name]};\n" lines << " char IgnoreArg_#{arg[:name]};\n"
end end
lines lines
end end
def mock_function_declarations(function) def mock_function_declarations(function)
lines = "" lines = ''
function[:args].each do |arg| function[:args].each do |arg|
lines << "#define #{function[:name]}_IgnoreArg_#{arg[:name]}()" lines << "#define #{function[:name]}_IgnoreArg_#{arg[:name]}()"
lines << " #{function[:name]}_CMockIgnoreArg_#{arg[:name]}(__LINE__)\n" lines << " #{function[:name]}_CMockIgnoreArg_#{arg[:name]}(__LINE__)\n"
@ -31,8 +31,8 @@ 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 << " 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"
lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 1;\n" lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 1;\n"
lines << "}\n\n" lines << "}\n\n"

View File

@ -2,41 +2,41 @@ class CMockGeneratorPluginReturnThruPtr
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 = 9 @priority = 9
end end
def instance_typedefs(function) def instance_typedefs(function)
lines = "" lines = ''
function[:args].each do |arg| function[:args].each do |arg|
if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
lines << " int ReturnThruPtr_#{arg[:name]}_Used;\n"
lines << " #{arg[:type]} ReturnThruPtr_#{arg[:name]}_Val;\n" lines << " char ReturnThruPtr_#{arg[:name]}_Used;\n"
lines << " int ReturnThruPtr_#{arg[:name]}_Size;\n" lines << " #{arg[:type]} ReturnThruPtr_#{arg[:name]}_Val;\n"
end lines << " size_t ReturnThruPtr_#{arg[:name]}_Size;\n"
end end
lines lines
end end
def mock_function_declarations(function) def mock_function_declarations(function)
lines = "" lines = ''
function[:args].each do |arg| function[:args].each do |arg|
if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
lines << "#define #{function[:name]}_ReturnThruPtr_#{arg[:name]}(#{arg[:name]})"
# If the pointer type actually contains an asterisk, we can do sizeof the type (super safe), otherwise lines << "#define #{function[:name]}_ReturnThruPtr_#{arg[:name]}(#{arg[:name]})"
# we need to do a sizeof the dereferenced pointer (which could be a problem if give the wrong size # If the pointer type actually contains an asterisk, we can do sizeof the type (super safe), otherwise
if (arg[:type][-1] == '*') # we need to do a sizeof the dereferenced pointer (which could be a problem if give the wrong size
lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(#{arg[:type][0..-2]}))\n" lines << if arg[:type][-1] == '*'
else " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(#{arg[:type][0..-2]}))\n"
lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n" else
end " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n"
lines << "#define #{function[:name]}_ReturnArrayThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_len)" end
lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, (int)(cmock_len * (int)sizeof(*#{arg[:name]})))\n" lines << "#define #{function[:name]}_ReturnArrayThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_len)"
lines << "#define #{function[:name]}_ReturnMemThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_size)" lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_len * sizeof(*#{arg[:name]}))\n"
lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_size)\n" lines << "#define #{function[:name]}_ReturnMemThruPtr_#{arg[:name]}(#{arg[:name]}, cmock_size)"
lines << "void #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg[:name]}, int cmock_size);\n" lines << " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, cmock_size)\n"
end lines << "void #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg[:name]}, size_t cmock_size);\n"
end end
lines lines
end end
@ -46,17 +46,17 @@ class CMockGeneratorPluginReturnThruPtr
func_name = function[:name] func_name = function[:name]
function[:args].each do |arg| function[:args].each do |arg|
arg_name = arg[:name] arg_name = arg[:name]
if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg_name}, int cmock_size)\n"
lines << "{\n" lines << "void #{func_name}_CMockReturnMemThruPtr_#{arg_name}(UNITY_LINE_TYPE cmock_line, #{arg[:type]} #{arg_name}, size_t cmock_size)\n"
lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " + lines << "{\n"
"(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n" lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = " \
lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp);\n" "(CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.#{func_name}_CallInstance));\n"
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used = 1;\n" lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp);\n"
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Val = #{arg_name};\n" lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Used = 1;\n"
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size = cmock_size;\n" lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Val = #{arg_name};\n"
lines << "}\n\n" lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size = cmock_size;\n"
end lines << "}\n\n"
end end
lines lines
end end
@ -65,14 +65,14 @@ class CMockGeneratorPluginReturnThruPtr
lines = [] lines = []
function[:args].each do |arg| function[:args].each do |arg|
arg_name = arg[:name] arg_name = arg[:name]
if (@utils.ptr_or_str?(arg[:type]) and not arg[:const?]) next unless @utils.ptr_or_str?(arg[:type]) && !(arg[:const?])
lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n"
lines << " {\n" lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n"
lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n" lines << " {\n"
lines << " memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n" lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n"
lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n" lines << " memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n"
lines << " }\n" lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n"
end lines << " }\n"
end end
lines lines
end end

View File

@ -5,10 +5,9 @@
# ========================================== # ==========================================
class CMockGeneratorUtils class CMockGeneratorUtils
attr_accessor :config, :helpers, :ordered, :ptr_handling, :arrays, :cexception attr_accessor :config, :helpers, :ordered, :ptr_handling, :arrays, :cexception
def initialize(config, helpers={}) def initialize(config, helpers = {})
@config = config @config = config
@ptr_handling = @config.when_ptr @ptr_handling = @config.when_ptr
@ordered = @config.enforce_strict_ordering @ordered = @config.enforce_strict_ordering
@ -16,9 +15,10 @@ class CMockGeneratorUtils
@cexception = @config.plugins.include? :cexception @cexception = @config.plugins.include? :cexception
@expect_any = @config.plugins.include? :expect_any_args @expect_any = @config.plugins.include? :expect_any_args
@return_thru_ptr = @config.plugins.include? :return_thru_ptr @return_thru_ptr = @config.plugins.include? :return_thru_ptr
@ignore_arg = @config.plugins.include? :ignore_arg @ignore_arg = @config.plugins.include? :ignore_arg
@ignore = @config.plugins.include? :ignore @ignore = @config.plugins.include? :ignore
@treat_as = @config.treat_as @ignore_stateless = @config.plugins.include? :ignore_stateless
@treat_as = @config.treat_as
@helpers = helpers @helpers = helpers
end end
@ -36,78 +36,78 @@ class CMockGeneratorUtils
end end
def code_verify_an_arg_expectation(function, arg) def code_verify_an_arg_expectation(function, arg)
if (@arrays) if @arrays
case(@ptr_handling) case @ptr_handling
when :smart then code_verify_an_arg_expectation_with_smart_arrays(function, arg) when :smart then code_verify_an_arg_expectation_with_smart_arrays(function, arg)
when :compare_data then code_verify_an_arg_expectation_with_normal_arrays(function, arg) when :compare_data then code_verify_an_arg_expectation_with_normal_arrays(function, arg)
when :compare_ptr then raise "ERROR: the array plugin doesn't enjoy working with :compare_ptr only. Disable one option." when :compare_ptr then raise "ERROR: the array plugin doesn't enjoy working with :compare_ptr only. Disable one option."
end end
else else
code_verify_an_arg_expectation_with_no_arrays(function, arg) code_verify_an_arg_expectation_with_no_arrays(function, arg)
end end
end end
def code_add_base_expectation(func_name, global_ordering_supported=true) def code_add_base_expectation(func_name, global_ordering_supported = true)
lines = " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n" lines = " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n"
lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n"
lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n"
lines << " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" lines << " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n"
lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n" lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n"
lines << " Mock.#{func_name}_IgnoreBool = (int)0;\n" if (@ignore) lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if @ignore || @ignore_stateless
lines << " cmock_call_instance->LineNumber = cmock_line;\n" lines << " cmock_call_instance->LineNumber = cmock_line;\n"
lines << " cmock_call_instance->CallOrder = ++GlobalExpectCount;\n" if (@ordered and global_ordering_supported) lines << " cmock_call_instance->CallOrder = ++GlobalExpectCount;\n" if @ordered && global_ordering_supported
lines << " cmock_call_instance->ExceptionToThrow = CEXCEPTION_NONE;\n" if (@cexception) lines << " cmock_call_instance->ExceptionToThrow = CEXCEPTION_NONE;\n" if @cexception
lines << " cmock_call_instance->ExpectAnyArgsBool = (int)0;\n" if (@expect_any) lines << " cmock_call_instance->ExpectAnyArgsBool = (char)0;\n" if @expect_any
lines lines
end end
def code_add_an_arg_expectation(arg, depth=1) def code_add_an_arg_expectation(arg, depth = 1)
lines = code_assign_argument_quickly("cmock_call_instance->Expected_#{arg[:name]}", arg) lines = code_assign_argument_quickly("cmock_call_instance->Expected_#{arg[:name]}", arg)
lines << " cmock_call_instance->Expected_#{arg[:name]}_Depth = #{arg[:name]}_Depth;\n" if (@arrays and (depth.class == String)) lines << " cmock_call_instance->Expected_#{arg[:name]}_Depth = #{arg[:name]}_Depth;\n" if @arrays && (depth.class == String)
lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 0;\n" if (@ignore_arg) lines << " cmock_call_instance->IgnoreArg_#{arg[:name]} = 0;\n" if @ignore_arg
lines << " cmock_call_instance->ReturnThruPtr_#{arg[:name]}_Used = 0;\n" if (@return_thru_ptr and ptr_or_str?(arg[:type]) and not arg[:const?]) lines << " cmock_call_instance->ReturnThruPtr_#{arg[:name]}_Used = 0;\n" if @return_thru_ptr && ptr_or_str?(arg[:type]) && !(arg[:const?])
lines lines
end end
def code_assign_argument_quickly(dest, arg) def code_assign_argument_quickly(dest, arg)
if (arg[:ptr?] or @treat_as.include?(arg[:type])) if arg[:ptr?] || @treat_as.include?(arg[:type])
" #{dest} = #{arg[:name]};\n" " #{dest} = #{arg[:name]};\n"
else else
assert_expr = "sizeof(#{arg[:name]}) == sizeof(#{arg[:type]}) ? 1 : -1" assert_expr = "sizeof(#{arg[:name]}) == sizeof(#{arg[:type]}) ? 1 : -1"
comment = "/* add #{arg[:type]} to :treat_as_array if this causes an error */" comment = "/* add #{arg[:type]} to :treat_as_array if this causes an error */"
" memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" + " memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" \
" sizeof(#{arg[:type]}[#{assert_expr}])); #{comment}\n" " sizeof(#{arg[:type]}[#{assert_expr}])); #{comment}\n"
end end
end end
def code_add_argument_loader(function) def code_add_argument_loader(function)
if (function[:args_string] != "void") if function[:args_string] != 'void'
if (@arrays) if @arrays
args_string = function[:args].map do |m| args_string = function[:args].map do |m|
type = arg_type_with_const(m) type = arg_type_with_const(m)
m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}"
end.join(', ') end.join(', ')
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string});\n" + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string});\n" \
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\n" + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\n" +
function[:args].inject("") { |all, arg| all + code_add_an_arg_expectation(arg, (arg[:ptr?] ? "#{arg[:name]}_Depth" : 1) ) } + function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg, (arg[:ptr?] ? "#{arg[:name]}_Depth" : 1)) } +
"}\n\n" "}\n\n"
else else
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]});\n" + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]});\n" \
"void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]})\n{\n" + "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{function[:args_string]})\n{\n" +
function[:args].inject("") { |all, arg| all + code_add_an_arg_expectation(arg) } + function[:args].inject('') { |all, arg| all + code_add_an_arg_expectation(arg) } +
"}\n\n" "}\n\n"
end end
else else
"" ''
end end
end end
def code_call_argument_loader(function) def code_call_argument_loader(function)
if (function[:args_string] != "void") if function[:args_string] != 'void'
args = function[:args].map do |m| args = function[:args].map do |m|
if (@arrays and m[:ptr?] and not m[:array_data?]) if @arrays && m[:ptr?] && !(m[:array_data?])
"#{m[:name]}, 1" "#{m[:name]}, 1"
elsif (@arrays and m[:array_size?]) elsif @arrays && m[:array_size?]
"#{m[:name]}, #{m[:name]}" "#{m[:name]}, #{m[:name]}"
else else
m[:name] m[:name]
@ -115,60 +115,60 @@ class CMockGeneratorUtils
end end
" CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n" " CMockExpectParameters_#{function[:name]}(cmock_call_instance, #{args.join(', ')});\n"
else else
"" ''
end end
end end
def ptr_or_str?(arg_type) def ptr_or_str?(arg_type)
return (arg_type.include? '*' or (arg_type.include?('*') ||
@treat_as.fetch(arg_type, "").include? '*') @treat_as.fetch(arg_type, '').include?('*'))
end end
#private ###################### # private ######################
def lookup_expect_type(function, arg) def lookup_expect_type(_function, arg)
c_type = arg[:type] c_type = arg[:type]
arg_name = arg[:name] arg_name = arg[:name]
expected = "cmock_call_instance->Expected_#{arg_name}" expected = "cmock_call_instance->Expected_#{arg_name}"
ignore = "cmock_call_instance->IgnoreArg_#{arg_name}" ignore = "cmock_call_instance->IgnoreArg_#{arg_name}"
unity_func = if ((arg[:ptr?]) and ((c_type =~ /\*\*/) or (@ptr_handling == :compare_ptr))) unity_func = if (arg[:ptr?]) && ((c_type =~ /\*\*/) || (@ptr_handling == :compare_ptr))
['UNITY_TEST_ASSERT_EQUAL_PTR', ''] ['UNITY_TEST_ASSERT_EQUAL_PTR', '']
else else
(@helpers.nil? or @helpers[:unity_helper].nil?) ? ["UNITY_TEST_ASSERT_EQUAL",''] : @helpers[:unity_helper].get_helper(c_type) @helpers.nil? || @helpers[:unity_helper].nil? ? ['UNITY_TEST_ASSERT_EQUAL', ''] : @helpers[:unity_helper].get_helper(c_type)
end end
return c_type, arg_name, expected, ignore, unity_func[0], unity_func[1] [c_type, arg_name, expected, ignore, unity_func[0], unity_func[1]]
end end
def code_verify_an_arg_expectation_with_no_arrays(function, arg) def code_verify_an_arg_expectation_with_no_arrays(function, arg)
c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg)
lines = "" lines = ''
lines << " if (!#{ignore})\n" if @ignore_arg lines << " if (!#{ignore})\n" if @ignore_arg
lines << " {\n" lines << " {\n"
lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n"
case(unity_func) case unity_func
when "UNITY_TEST_ASSERT_EQUAL_MEMORY" when 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
c_type_local = c_type.gsub(/\*$/,'') c_type_local = c_type.gsub(/\*$/, '')
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n"
when "UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY" when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY'
if (pre == '&') if pre == '&'
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), cmock_line, CMockStringMismatch);\n" lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), cmock_line, CMockStringMismatch); }\n"
end
when /_ARRAY/
if (pre == '&')
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch); }\n"
end
else else
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch); }\n"
end
when /_ARRAY/
if pre == '&'
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, 1, cmock_line, CMockStringMismatch); }\n"
end
else
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n"
end end
lines << " }\n" lines << " }\n"
lines lines
@ -176,78 +176,75 @@ class CMockGeneratorUtils
def code_verify_an_arg_expectation_with_normal_arrays(function, arg) def code_verify_an_arg_expectation_with_normal_arrays(function, arg)
c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg)
depth_name = (arg[:ptr?]) ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1
lines = "" lines = ''
lines << " if (!#{ignore})\n" if @ignore_arg lines << " if (!#{ignore})\n" if @ignore_arg
lines << " {\n" lines << " {\n"
lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n"
lines << " if (#{pre}#{expected} != #{pre}#{arg_name}) {\n" case unity_func
case(unity_func) when 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
when "UNITY_TEST_ASSERT_EQUAL_MEMORY" c_type_local = c_type.gsub(/\*$/, '')
c_type_local = c_type.gsub(/\*$/,'') lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n"
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY'
when "UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY" if pre == '&'
if (pre == '&') lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), cmock_line, CMockStringMismatch);\n"
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
when /_ARRAY/
if (pre == '&')
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
else else
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
when /_ARRAY/
if pre == '&'
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << " else\n"
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
else
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n"
end end
lines << " }\n }\n" lines << " }\n"
lines lines
end end
def code_verify_an_arg_expectation_with_smart_arrays(function, arg) def code_verify_an_arg_expectation_with_smart_arrays(function, arg)
c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg) c_type, arg_name, expected, ignore, unity_func, pre = lookup_expect_type(function, arg)
depth_name = (arg[:ptr?]) ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1 depth_name = arg[:ptr?] ? "cmock_call_instance->Expected_#{arg_name}_Depth" : 1
lines = "" lines = ''
lines << " if (!#{ignore})\n" if @ignore_arg lines << " if (!#{ignore})\n" if @ignore_arg
lines << " {\n" lines << " {\n"
lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n" lines << " UNITY_SET_DETAILS(CMockString_#{function[:name]},CMockString_#{arg_name});\n"
lines << " if (#{pre}#{expected} != #{pre}#{arg_name}) {\n" case unity_func
case(unity_func) when 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
when "UNITY_TEST_ASSERT_EQUAL_MEMORY" c_type_local = c_type.gsub(/\*$/, '')
c_type_local = c_type.gsub(/\*$/,'') lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n"
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type_local}), cmock_line, CMockStringMismatch);\n" when 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY'
when "UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY" if pre == '&'
if (pre == '&') lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch);\n"
lines << " UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), #{depth_name}, cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << ((depth_name != 1) ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : "")
lines << " else\n"
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*','')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
when /_ARRAY/
if (pre == '&')
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << ((depth_name != 1) ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : "")
lines << " else\n"
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
else else
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n" lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '')
lines << " else\n"
lines << " { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(#{pre}#{expected}), (void*)(#{pre}#{arg_name}), sizeof(#{c_type.sub('*', '')}), #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
when /_ARRAY/
if pre == '&'
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch);\n"
else
lines << " if (#{pre}#{expected} == NULL)\n"
lines << " { UNITY_TEST_ASSERT_NULL(#{pre}#{arg_name}, cmock_line, CMockStringExpNULL); }\n"
lines << (depth_name != 1 ? " else if (#{depth_name} == 0)\n { UNITY_TEST_ASSERT_EQUAL_PTR(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch); }\n" : '')
lines << " else\n"
lines << " { #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, #{depth_name}, cmock_line, CMockStringMismatch); }\n"
end
else
lines << " #{unity_func}(#{pre}#{expected}, #{pre}#{arg_name}, cmock_line, CMockStringMismatch);\n"
end end
lines << " }\n }\n" lines << " }\n"
lines lines
end end
end end

View File

@ -5,54 +5,217 @@
# ========================================== # ==========================================
class CMockHeaderParser class CMockHeaderParser
attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns
attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs
def initialize(cfg) def initialize(cfg)
@funcs = []
@c_strippables = cfg.strippables @c_strippables = cfg.strippables
@c_attr_noconst = cfg.attributes.uniq - ['const'] @c_attr_noconst = cfg.attributes.uniq - ['const']
@c_attributes = ['const'] + c_attr_noconst @c_attributes = ['const'] + c_attr_noconst
@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
@declaration_parse_matcher = /([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+-]*)\)$/m @function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]+??)\(([\w\s\*\(\),\.\[\]+\-\/]*)\)'
@standards = (['int','short','char','long','unsigned','signed'] + cfg.treat_as.keys).uniq @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 @array_size_name = cfg.array_size_name
@array_size_type = (['int', 'size_t'] + cfg.array_size_type).uniq @array_size_type = (%w[int size_t] + cfg.array_size_type).uniq
@when_no_prototypes = cfg.when_no_prototypes @when_no_prototypes = cfg.when_no_prototypes
@local_as_void = @treat_as_void @local_as_void = @treat_as_void
@verbosity = cfg.verbosity @verbosity = cfg.verbosity
@treat_externs = cfg.treat_externs @treat_externs = cfg.treat_externs
@c_strippables += ['extern'] if (@treat_externs == :include) #we'll need to remove the attribute if we're allowing externs @treat_inlines = cfg.treat_inlines
@inline_function_patterns = cfg.inline_function_patterns
@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
end end
def parse(name, source) def parse(name, source)
@module_name = name.gsub(/\W/,'') parse_project = {
@typedefs = [] :module_name => name.gsub(/\W/, ''),
@funcs = [] :typedefs => [],
:functions => [],
:normalized_source => nil
}
function_names = [] function_names = []
parse_functions( import_source(source) ).map do |decl| all_funcs = parse_functions(import_source(source, parse_project)).map { |item| [item] }
func = parse_declaration(decl) all_funcs += parse_cpp_functions(import_source(source, parse_project, true))
unless (function_names.include? func[:name]) all_funcs.map do |decl|
@funcs << func func = parse_declaration(parse_project, *decl)
unless function_names.include? func[:name]
parse_project[:functions] << func
function_names << func[:name] function_names << func[:name]
end end
end end
parse_project[:normalized_source] = if @treat_inlines == :include
transform_inline_functions(source)
else
''
end
{ :includes => nil, { :includes => nil,
:functions => @funcs, :functions => parse_project[:functions],
:typedefs => @typedefs :typedefs => parse_project[:typedefs],
} :normalized_source => parse_project[:normalized_source] }
end end
private if $ThisIsOnlyATest.nil? ################ private if $ThisIsOnlyATest.nil? ################
def import_source(source) # Remove C/C++ comments from a string
# +source+:: String which will have the comments removed
def remove_comments_from_source(source)
# remove comments (block and line, in three steps to ensure correct precedence)
source.gsub!(/(?<!\*)\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks
source.gsub!(/\/\*.*?\*\//m, '') # remove block comments
source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain)
end
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
end
source
end
# Return the number of pairs of braces/square brackets in the function provided by the user
# +source+:: String containing the function to be processed
def count_number_of_pairs_of_braces_in_function(source)
is_function_start_found = false
curr_level = 0
total_pairs = 0
source.each_char do |c|
if c == '{'
curr_level += 1
total_pairs += 1
is_function_start_found = true
elsif c == '}'
curr_level -= 1
end
break if is_function_start_found && curr_level == 0 # We reached the end of the inline function body
end
if curr_level != 0
total_pairs = 0 # Something is fishy about this source, not enough closing braces?
end
total_pairs
end
# Transform inline functions to regular functions in the source by the user
# +source+:: String containing the source to be processed
def transform_inline_functions(source)
inline_function_regex_formats = []
square_bracket_pair_regex_format = /\{[^\{\}]*\}/ # Regex to match one whole block enclosed by two square brackets
# Convert user provided string patterns to regex
# Use word bounderies before and after the user regex to limit matching to actual word iso part of a word
@inline_function_patterns.each do |user_format_string|
user_regex = Regexp.new(user_format_string)
word_boundary_before_user_regex = /\b/
cleanup_spaces_after_user_regex = /[ ]*\b/
inline_function_regex_formats << Regexp.new(word_boundary_before_user_regex.source + user_regex.source + cleanup_spaces_after_user_regex.source)
end
# let's clean up the encoding in case they've done anything weird with the characters we might find # let's clean up the encoding in case they've done anything weird with the characters we might find
source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil) source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil)
# Comments can contain words that will trigger the parser (static|inline|<user_defined_static_keyword>)
remove_comments_from_source(source)
# smush multiline macros into single line (checking for continuation character at end of line '\')
# If the user uses a macro to declare an inline function,
# smushing the macros makes it easier to recognize them as a macro and if required,
# remove them later on in this function
source.gsub!(/\s*\\\s*/m, ' ')
# Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter
# Instead, look for an inline pattern (f.e. "static inline") and parse it.
# Below is a small explanation on how the general mechanism works:
# - Everything before the match should just be copied, we don't want
# to touch anything but the inline functions.
# - Remove the implementation of the inline function (this is enclosed
# in square brackets) and replace it with ";" to complete the
# transformation to normal/non-inline function.
# To ensure proper removal of the function body, we count the number of square-bracket pairs
# and remove the pairs one-by-one.
# - Copy everything after the inline function implementation and start the parsing of the next inline function
# There are ofcourse some special cases (inline macro declarations, inline function declarations, ...) which are handled and explained below
inline_function_regex_formats.each do |format|
inspected_source = ''
regex_matched = false
loop do
inline_function_match = source.match(/#{format}/) # Search for inline function declaration
if inline_function_match.nil? # No inline functions so nothing to do
# Join pre and post match stripped parts for the next inline function detection regex
source = inspected_source + source if regex_matched == true
break
end
regex_matched = true
# 1. Determine if we are dealing with a user defined macro to declare inline functions
# If the end of the pre-match string is a macro-declaration-like string,
# we are dealing with a user defined macro to declare inline functions
if /(#define\s*)\z/ =~ inline_function_match.pre_match
# Remove the macro from the source
stripped_pre_match = inline_function_match.pre_match.sub(/(#define\s*)\z/, '')
stripped_post_match = inline_function_match.post_match.sub(/\A(.*[\n]?)/, '')
inspected_source += stripped_pre_match
source = stripped_post_match
next
end
# 2. Determine if we are dealing with an inline function declaration iso function definition
# If the start of the post-match string is a function-declaration-like string (something ending with semicolon after the function arguments),
# we are dealing with a inline function declaration
if /\A#{@function_declaration_parse_base_match}\s*;/m =~ inline_function_match.post_match
# Only remove the inline part from the function declaration, leaving the function declaration won't do any harm
inspected_source += inline_function_match.pre_match
source = inline_function_match.post_match
next
end
# 3. If we get here, we found an inline function declaration AND inline function body.
# Remove the function body to transform it into a 'normal' function declaration.
if /\A#{@function_declaration_parse_base_match}\s*\{/m =~ inline_function_match.post_match
total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match)
break if total_pairs_to_remove == 0 # Bad source?
inline_function_stripped = inline_function_match.post_match
total_pairs_to_remove.times do
inline_function_stripped.sub!(/\s*#{square_bracket_pair_regex_format}/, ';') # Remove inline implementation (+ some whitespace because it's prettier)
end
inspected_source += inline_function_match.pre_match
source = inline_function_stripped
next
end
# 4. If we get here, it means the regex match, but it is not related to the function (ex. static variable in header)
# Leave this code as it is.
inspected_source += inline_function_match.pre_match + inline_function_match[0]
source = inline_function_match.post_match
end
end
source
end
def import_source(source, parse_project, cpp = false)
# let's clean up the encoding in case they've done anything weird with the characters we might find
source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil)
# void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void # void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void
# to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void # to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void
@ -62,13 +225,18 @@ class CMockHeaderParser
@local_as_void += void_types.flatten.uniq.compact @local_as_void += void_types.flatten.uniq.compact
end end
# If user wants to mock inline functions,
# remove the (user specific) inline keywords before removing anything else to avoid missing an inline function
if @treat_inlines == :include
@inline_function_patterns.each do |user_format_string|
source.gsub!(/#{user_format_string}/, '') # remove user defined inline function patterns
end
end
# smush multiline macros into single line (checking for continuation character at end of line '\') # smush multiline macros into single line (checking for continuation character at end of line '\')
source.gsub!(/\s*\\\s*/m, ' ') source.gsub!(/\s*\\\s*/m, ' ')
#remove comments (block and line, in three steps to ensure correct precedence) remove_comments_from_source(source)
source.gsub!(/(?<!\*)\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks
source.gsub!(/\/\*.*?\*\//m, '') # remove block comments
source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain)
# remove assembler pragma sections # remove assembler pragma sections
source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '')
@ -84,66 +252,123 @@ class CMockHeaderParser
# forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes # forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes
source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs
source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces
source.gsub!(/(\W)(?:register|auto|static|restrict)(\W)/, '\1\2') # remove problem keywords # remove problem keywords
source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2')
source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp
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
source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements
source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric
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+\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|
functype = "cmock_#{@module_name}_func_ptr#{@typedefs.size + 1}" functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"
@typedefs << "typedef #{$1.strip}(*#{functype})(#{$4});" unless cpp # only collect once
"#{functype} #{$2.strip}(#{$3});" parse_project[:typedefs] << "typedef #{Regexp.last_match(1).strip}(*#{functype})(#{Regexp.last_match(4)});"
end "#{functype} #{Regexp.last_match(2).strip}(#{Regexp.last_match(3)});"
# 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 end
end end
# remove function definitions by stripping off the arguments right now source = remove_nested_pairs_of_braces(source) unless cpp
source.gsub!(/\([^\)]*\)\s*\{[^\}]*\}/m, ";")
#drop extra white space to make the rest go faster if @treat_inlines == :include
# Functions having "{ }" at this point are/were inline functions,
# User wants them in so 'disguise' them as normal functions with the ";"
source.gsub!('{ }', ';')
end
# remove function definitions by stripping off the arguments right now
source.gsub!(/\([^\)]*\)\s*\{[^\}]*\}/m, ';')
# drop extra white space to make the rest go faster
source.gsub!(/^\s+/, '') # remove extra white space from beginning of line source.gsub!(/^\s+/, '') # remove extra white space from beginning of line
source.gsub!(/\s+$/, '') # remove extra white space from end of line source.gsub!(/\s+$/, '') # remove extra white space from end of line
source.gsub!(/\s*\(\s*/, '(') # remove extra white space from before left parens source.gsub!(/\s*\(\s*/, '(') # remove extra white space from before left parens
source.gsub!(/\s*\)\s*/, ')') # remove extra white space from before right parens source.gsub!(/\s*\)\s*/, ')') # remove extra white space from before right parens
source.gsub!(/\s+/, ' ') # remove remaining extra white space source.gsub!(/\s+/, ' ') # remove remaining extra white space
#split lines on semicolons and remove things that are obviously not what we are looking for # split lines on semicolons and remove things that are obviously not what we are looking for
src_lines = source.split(/\s*;\s*/).uniq src_lines = source.split(/\s*;\s*/)
src_lines.delete_if {|line| line.strip.length == 0} # remove blank lines src_lines = src_lines.uniq unless cpp # must retain closing braces for class/namespace
src_lines.delete_if {|line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil?} #remove function pointer arrays src_lines.delete_if { |line| line.strip.empty? } # remove blank lines
if (@treat_externs == :include) src_lines.delete_if { |line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil? } # remove function pointer arrays
src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?} # remove inline functions
else unless @treat_externs == :include
src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern|inline)\s+/).nil?} # remove inline and extern functions src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:extern)\s+/).nil? } # remove extern functions
end end
src_lines.delete_if {|line| line.empty? } #drop empty lines
unless @treat_inlines == :include
src_lines.delete_if { |line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil? } # remove inline functions
end
src_lines.delete_if(&:empty?) # drop empty lines
end
# Rudimentary C++ parser - does not handle all situations - e.g.:
# * A namespace function appears after a class with private members (should be parsed)
# * Anonymous namespace (shouldn't parse anything - no matter how nested - within it)
# * A class nested within another class
def parse_cpp_functions(source)
funcs = []
ns = []
pub = false
source.each do |line|
# Search for namespace, class, opening and closing braces
line.scan(/(?:(?:\b(?:namespace|class)\s+(?:\S+)\s*)?{)|}/).each do |item|
if item == '}'
ns.pop
else
token = item.strip.sub(/\s+/, ' ')
ns << token
pub = false if token.start_with? 'class'
pub = true if token.start_with? 'namespace'
end
end
pub = true if line =~ /public:/
pub = false if line =~ /private:/ || line =~ /protected:/
# ignore non-public and non-static
next unless pub
next unless line =~ /\bstatic\b/
line.sub!(/^.*static/, '')
next unless line =~ @declaration_parse_matcher
tmp = ns.reject { |item| item == '{' }
# Identify class name, if any
cls = nil
if tmp[-1].start_with? 'class '
cls = tmp.pop.sub(/class (\S+) {/, '\1')
end
# Assemble list of namespaces
tmp.each { |item| item.sub!(/(?:namespace|class) (\S+) {/, '\1') }
funcs << [line.strip.gsub(/\s+/, ' '), tmp, cls]
end
funcs
end end
def parse_functions(source) def parse_functions(source)
funcs = [] funcs = []
source.each {|line| funcs << line.strip.gsub(/\s+/, ' ') if (line =~ @declaration_parse_matcher)} source.each { |line| funcs << line.strip.gsub(/\s+/, ' ') if line =~ @declaration_parse_matcher }
if funcs.empty? if funcs.empty?
case @when_no_prototypes case @when_no_prototypes
when :error when :error
raise "ERROR: No function prototypes found!" raise 'ERROR: No function prototypes found!'
when :warn when :warn
puts "WARNING: No function prototypes found!" unless (@verbosity < 1) puts 'WARNING: No function prototypes found!' unless @verbosity < 1
end end
end end
return funcs funcs
end end
def parse_type_and_name(arg) def parse_type_and_name(arg)
@ -151,8 +376,8 @@ class CMockHeaderParser
# to remove 'const' only when it applies to the pointer itself, not when it # to remove 'const' only when it applies to the pointer itself, not when it
# applies to the type pointed to. For non-pointer types, remove any # applies to the type pointed to. For non-pointer types, remove any
# occurrence of 'const'. # occurrence of 'const'.
arg.gsub!(/(\w)\*/,'\1 *') # pull asterisks away from preceding word arg.gsub!(/(\w)\*/, '\1 *') # pull asterisks away from preceding word
arg.gsub!(/\*(\w)/,'* \1') # pull asterisks away from following word arg.gsub!(/\*(\w)/, '* \1') # pull asterisks away from following word
arg_array = arg.split arg_array = arg.split
arg_info = divine_ptr_and_const(arg) arg_info = divine_ptr_and_const(arg)
arg_info[:name] = arg_array[-1] arg_info[:name] = arg_array[-1]
@ -177,15 +402,15 @@ class CMockHeaderParser
end end
arg_info[:modifier] = attr_array.join(' ') arg_info[:modifier] = attr_array.join(' ')
arg_info[:type] = type_array.join(' ').gsub(/\s+\*/,'*') # remove space before asterisks arg_info[:type] = type_array.join(' ').gsub(/\s+\*/, '*') # remove space before asterisks
return arg_info arg_info
end end
def parse_args(arg_list) def parse_args(arg_list)
args = [] args = []
arg_list.split(',').each do |arg| arg_list.split(',').each do |arg|
arg.strip! arg.strip!
return args if (arg =~ /^\s*((\.\.\.)|(void))\s*$/) # we're done if we reach void by itself or ... return args if arg =~ /^\s*((\.\.\.)|(void))\s*$/ # we're done if we reach void by itself or ...
arg_info = parse_type_and_name(arg) arg_info = parse_type_and_name(arg)
arg_info.delete(:modifier) # don't care about this arg_info.delete(:modifier) # don't care about this
@ -193,7 +418,7 @@ class CMockHeaderParser
# in C, array arguments implicitly degrade to pointers # in C, array arguments implicitly degrade to pointers
# make the translation explicit here to simplify later logic # make the translation explicit here to simplify later logic
if @treat_as_array[arg_info[:type]] and not arg_info[:ptr?] then if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?])
arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*"
arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?]
arg_info[:ptr?] = true arg_info[:ptr?] = true
@ -203,31 +428,35 @@ class CMockHeaderParser
end end
# Try to find array pair in parameters following this pattern : <type> * <name>, <@array_size_type> <@array_size_name> # Try to find array pair in parameters following this pattern : <type> * <name>, <@array_size_type> <@array_size_name>
args.each_with_index {|val, index| args.each_with_index do |val, index|
next_index = index + 1 next_index = index + 1
if (args.length > next_index) next unless args.length > next_index
if (val[:ptr?] == true and args[next_index][:name].match(@array_size_name) and @array_size_type.include?(args[next_index][:type]))
val[:array_data?] = true
args[next_index][:array_size?] = true
end
end
}
return args if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type])
val[:array_data?] = true
args[next_index][:array_size?] = true
end
end
args
end end
def divine_ptr(arg) def divine_ptr(arg)
return false unless arg.include? '*' return false unless arg.include? '*'
# treat "const char *" and similar as a string, not a pointer # treat "const char *" and similar as a string, not a pointer
return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg
return true
true
end end
def divine_const(arg) def divine_const(arg)
# a non-pointer arg containing "const" is a constant # a non-pointer arg containing "const" is a constant
# an arg containing "const" before the last * is a pointer to a constant # an arg containing "const" before the last * is a pointer to a constant
return ( arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) if arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) : (/(^|\s)const(\s|$)/ =~ arg)
: (/(^|\s)const(\s|$)/ =~ arg) ) ? true : false true
else
false
end
end end
def divine_ptr_and_const(arg) def divine_ptr_and_const(arg)
@ -237,128 +466,158 @@ class CMockHeaderParser
divination[:const?] = divine_const(arg) divination[:const?] = divine_const(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
return divination divination
end end
def clean_args(arg_list) def clean_args(arg_list, parse_project)
if ((@local_as_void.include?(arg_list.strip)) or (arg_list.empty?)) if @local_as_void.include?(arg_list.strip) || arg_list.empty?
return 'void' 'void'
else else
c=0 c = 0
arg_list.gsub!(/(\w+)(?:\s*\[\s*\(*[\s\w+-]*\)*\s*\])+/,'*\1') # magically turn brackets into asterisks, also match for parentheses that come from macros # magically turn brackets into asterisks, also match for parentheses that come from macros
arg_list.gsub!(/\s+\*/,'*') # remove space to place asterisks with type (where they belong) arg_list.gsub!(/(\w+)(?:\s*\[[^\[\]]*\])+/, '*\1')
arg_list.gsub!(/\*(\w)/,'* \1') # pull asterisks away from arg to place asterisks with type (where they belong) # remove space to place asterisks with type (where they belong)
arg_list.gsub!(/\s+\*/, '*')
# pull asterisks away from arg to place asterisks with type (where they belong)
arg_list.gsub!(/\*(\w)/, '* \1')
#scan argument list for function pointers and replace them with custom types # scan argument list for function pointers and replace them with custom types
arg_list.gsub!(/([\w\s\*]+)\(+\s*\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |m| arg_list.gsub!(/([\w\s\*]+)\(+\s*\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m|
functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"
functype = "cmock_#{@module_name}_func_ptr#{@typedefs.size + 1}" funcret = Regexp.last_match(1).strip
funcret = $1.strip funcname = Regexp.last_match(2).strip
funcname = $2.strip funcargs = Regexp.last_match(3).strip
funcargs = $3.strip
funconst = '' funconst = ''
if (funcname.include? 'const') if funcname.include? 'const'
funcname.gsub!('const','').strip! funcname.gsub!('const', '').strip!
funconst = 'const ' funconst = 'const '
end end
@typedefs << "typedef #{funcret}(*#{functype})(#{funcargs});" parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});"
funcname = "cmock_arg#{c+=1}" if (funcname.empty?) funcname = "cmock_arg#{c += 1}" if funcname.empty?
"#{functype} #{funconst}#{funcname}" "#{functype} #{funconst}#{funcname}"
end end
#automatically name unnamed arguments (those that only had a type) # scan argument list for function pointers with shorthand notation and replace them with custom types
arg_list.split(/\s*,\s*/).map { |arg| arg_list.gsub!(/([\w\s\*]+)+\s+(\w+)\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m|
functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"
funcret = Regexp.last_match(1).strip
funcname = Regexp.last_match(2).strip
funcargs = Regexp.last_match(3).strip
funconst = ''
if funcname.include? 'const'
funcname.gsub!('const', '').strip!
funconst = 'const '
end
parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});"
funcname = "cmock_arg#{c += 1}" if funcname.empty?
"#{functype} #{funconst}#{funcname}"
end
# automatically name unnamed arguments (those that only had a type)
arg_list.split(/\s*,\s*/).map do |arg|
parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*']) parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*'])
if ((parts.size < 2) or (parts[-1][-1].chr == '*') or (@standards.include?(parts[-1]))) if (parts.size < 2) || (parts[-1][-1].chr == '*') || @standards.include?(parts[-1])
"#{arg} cmock_arg#{c+=1}" "#{arg} cmock_arg#{c += 1}"
else else
arg arg
end end
}.join(', ') end.join(', ')
end end
end end
def parse_declaration(declaration) def parse_declaration(parse_project, declaration, namespace = [], classname = nil)
decl = {} decl = {}
decl[:namespace] = namespace
decl[:class] = classname
regex_match = @declaration_parse_matcher.match(declaration) regex_match = @declaration_parse_matcher.match(declaration)
raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil? raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil?
#grab argument list # grab argument list
args = regex_match[2].strip args = regex_match[2].strip
#process function attributes, return type, and name # process function attributes, return type, and name
parsed = parse_type_and_name(regex_match[1]) parsed = parse_type_and_name(regex_match[1])
decl[:name] = parsed[:name] # Record original name without scope prefix
decl[:unscoped_name] = parsed[:name]
# Prefix name with namespace scope (if any) and then class
decl[:name] = namespace.join('_')
unless classname.nil?
decl[:name] << '_' unless decl[:name].empty?
decl[:name] << classname
end
# Add original name to complete fully scoped name
decl[:name] << '_' unless decl[:name].empty?
decl[:name] << decl[:unscoped_name]
decl[:modifier] = parsed[:modifier] decl[:modifier] = parsed[:modifier]
unless parsed[:c_calling_convention].nil? unless parsed[:c_calling_convention].nil?
decl[:c_calling_convention] = parsed[:c_calling_convention] decl[:c_calling_convention] = parsed[:c_calling_convention]
end end
rettype = parsed[:type] rettype = parsed[:type]
rettype = 'void' if (@local_as_void.include?(rettype.strip)) rettype = 'void' if @local_as_void.include?(rettype.strip)
decl[:return] = { :type => rettype, decl[:return] = { :type => rettype,
:name => 'cmock_to_return', :name => 'cmock_to_return',
:str => "#{rettype} cmock_to_return", :str => "#{rettype} cmock_to_return",
:void? => (rettype == 'void'), :void? => (rettype == 'void'),
:ptr? => parsed[:ptr?], :ptr? => parsed[:ptr?] || false,
:const? => parsed[:const?], :const? => parsed[:const?] || false,
:const_ptr? => parsed[:const_ptr?] :const_ptr? => parsed[:const_ptr?] || false }
}
#remove default argument statements from mock definitions # remove default argument statements from mock definitions
args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ') args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ')
#check for var args # check for var args
if (args =~ /\.\.\./) if args =~ /\.\.\./
decl[:var_arg] = args.match( /[\w\s]*\.\.\./ ).to_s.strip decl[:var_arg] = args.match(/[\w\s]*\.\.\./).to_s.strip
if (args =~ /\,[\w\s]*\.\.\./) args = if args =~ /\,[\w\s]*\.\.\./
args = args.gsub!(/\,[\w\s]*\.\.\./,'') args.gsub!(/\,[\w\s]*\.\.\./, '')
else else
args = 'void' 'void'
end end
else else
decl[:var_arg] = nil decl[:var_arg] = nil
end end
args = clean_args(args) args = clean_args(args, parse_project)
decl[:args_string] = args decl[:args_string] = args
decl[:args] = parse_args(args) decl[:args] = parse_args(args)
decl[:args_call] = decl[:args].map{|a| a[:name]}.join(', ') decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ')
decl[:contains_ptr?] = decl[:args].inject(false) {|ptr, arg| arg[:ptr?] ? true : ptr } decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr }
if (decl[:return][:type].nil? or decl[:name].nil? or decl[:args].nil? or if decl[:return][:type].nil? || decl[:name].nil? || decl[:args].nil? ||
decl[:return][:type].empty? or decl[:name].empty?) decl[:return][:type].empty? || decl[:name].empty?
raise "Failed Parsing Declaration Prototype!\n" + raise "Failed Parsing Declaration Prototype!\n" \
" declaration: '#{declaration}'\n" + " declaration: '#{declaration}'\n" \
" modifier: '#{decl[:modifier]}'\n" + " modifier: '#{decl[:modifier]}'\n" \
" return: #{prototype_inspect_hash(decl[:return])}\n" + " return: #{prototype_inspect_hash(decl[:return])}\n" \
" function: '#{decl[:name]}'\n" + " function: '#{decl[:name]}'\n" \
" args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n"
end end
return decl decl
end end
def prototype_inspect_hash(hash) def prototype_inspect_hash(hash)
pairs = [] pairs = []
hash.each_pair { |name, value| pairs << ":#{name} => #{"'" if (value.class == String)}#{value}#{"'" if (value.class == String)}" } hash.each_pair { |name, value| pairs << ":#{name} => #{"'" if value.class == String}#{value}#{"'" if value.class == String}" }
return "{#{pairs.join(', ')}}" "{#{pairs.join(', ')}}"
end end
def prototype_inspect_array_of_hashes(array) def prototype_inspect_array_of_hashes(array)
hashes = [] hashes = []
array.each { |hash| hashes << prototype_inspect_hash(hash) } array.each { |hash| hashes << prototype_inspect_hash(hash) }
case (array.size) case array.size
when 0 when 0
return "[]" return '[]'
when 1 when 1
return "[#{hashes[0]}]" return "[#{hashes[0]}]"
else else
return "[\n #{hashes.join("\n ")}\n ]\n" return "[\n #{hashes.join("\n ")}\n ]\n"
end end
end end
end end

View File

@ -4,10 +4,7 @@
# [Released under MIT License. Please refer to license.txt for details] # [Released under MIT License. Please refer to license.txt for details]
# ========================================== # ==========================================
require 'thread'
class CMockPluginManager class CMockPluginManager
attr_accessor :plugins attr_accessor :plugins
def initialize(config, utils) def initialize(config, utils)
@ -15,41 +12,39 @@ class CMockPluginManager
plugins_to_load = [:expect, config.plugins].flatten.uniq.compact plugins_to_load = [:expect, config.plugins].flatten.uniq.compact
plugins_to_load.each do |plugin| plugins_to_load.each do |plugin|
plugin_name = plugin.to_s plugin_name = plugin.to_s
object_name = "CMockGeneratorPlugin" + camelize(plugin_name) object_name = 'CMockGeneratorPlugin' + camelize(plugin_name)
self.class.plugin_require_mutex.synchronize { load_plugin(plugin_name, object_name, config, utils) } self.class.mutex.synchronize { load_plugin(plugin_name, object_name, config, utils) }
end end
@plugins.sort! {|a,b| a.priority <=> b.priority } @plugins.sort! { |a, b| a.priority <=> b.priority }
end end
def run(method, args=nil) def run(method, args = nil)
if args.nil? if args.nil?
return @plugins.collect{ |plugin| plugin.send(method) if plugin.respond_to?(method) }.flatten.join @plugins.collect { |plugin| plugin.send(method) if plugin.respond_to?(method) }.flatten.join
else else
return @plugins.collect{ |plugin| plugin.send(method, args) if plugin.respond_to?(method) }.flatten.join @plugins.collect { |plugin| plugin.send(method, args) if plugin.respond_to?(method) }.flatten.join
end end
end end
def camelize(lower_case_and_underscored_word) def camelize(lower_case_and_underscored_word)
lower_case_and_underscored_word.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase } lower_case_and_underscored_word.gsub(/\/(.?)/) { '::' + Regexp.last_match(1).upcase }.gsub(/(^|_)(.)/) { Regexp.last_match(2).upcase }
end end
private def self.mutex
def self.plugin_require_mutex
@mutex ||= Mutex.new @mutex ||= Mutex.new
end end
private
def load_plugin(plugin_name, object_name, config, utils) def load_plugin(plugin_name, object_name, config, utils)
begin unless Object.const_defined? object_name
unless (Object.const_defined? object_name) file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb"
file_name = "#{File.expand_path(File.dirname(__FILE__))}/cmock_generator_plugin_#{plugin_name.downcase}.rb" require file_name
require file_name
end
class_name = Object.const_get(object_name)
@plugins << class_name.new(config, utils)
rescue
file_name = "#{File.expand_path(File.dirname(__FILE__))}/cmock_generator_plugin_#{plugin_name.downcase}.rb"
raise "ERROR: CMock unable to load plugin '#{plugin_name}' '#{object_name}' #{file_name}"
end end
class_name = Object.const_get(object_name)
@plugins << class_name.new(config, utils)
rescue StandardError
file_name = "#{__dir__}/cmock_generator_plugin_#{plugin_name.downcase}.rb"
raise "ERROR: CMock unable to load plugin '#{plugin_name}' '#{object_name}' #{file_name}"
end end
end end

View File

@ -2,74 +2,76 @@
# CMock Project - Automatic Mock Generation for C # CMock Project - Automatic Mock Generation for C
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
# [Released under MIT License. Please refer to license.txt for details] # [Released under MIT License. Please refer to license.txt for details]
# ========================================== # ==========================================
class CMockUnityHelperParser class CMockUnityHelperParser
attr_accessor :c_types attr_accessor :c_types
def initialize(config) def initialize(config)
@config = config @config = config
@fallback = @config.plugins.include?(:array) ? 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' : 'UNITY_TEST_ASSERT_EQUAL_MEMORY' @fallback = @config.plugins.include?(:array) ? 'UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY' : 'UNITY_TEST_ASSERT_EQUAL_MEMORY'
@c_types = map_C_types.merge(import_source) @c_types = map_c_types.merge(import_source)
end end
def get_helper(ctype) def get_helper(ctype)
lookup = ctype.gsub(/(?:^|(\S?)(\s*)|(\W))const(?:$|(\s*)(\S)|(\W))/,'\1\3\5\6').strip.gsub(/\s+/,'_') lookup = ctype.gsub(/(?:^|(\S?)(\s*)|(\W))const(?:$|(\s*)(\S)|(\W))/, '\1\3\5\6').strip.gsub(/\s+/, '_')
return [@c_types[lookup], ''] if (@c_types[lookup]) return [@c_types[lookup], ''] if @c_types[lookup]
if (lookup =~ /\*$/)
lookup = lookup.gsub(/\*$/,'') if lookup =~ /\*$/
return [@c_types[lookup], '*'] if (@c_types[lookup]) lookup = lookup.gsub(/\*$/, '')
return [@c_types[lookup], '*'] if @c_types[lookup]
else else
lookup = lookup + '*' lookup += '*'
return [@c_types[lookup], '&'] if (@c_types[lookup]) return [@c_types[lookup], '&'] if @c_types[lookup]
end end
return ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] if (ctype =~ /cmock_\w+_ptr\d+/) return ['UNITY_TEST_ASSERT_EQUAL_PTR', ''] if ctype =~ /cmock_\w+_ptr\d+/
raise("Don't know how to test #{ctype} and memory tests are disabled!") unless @config.memcmp_if_unknown raise("Don't know how to test #{ctype} and memory tests are disabled!") unless @config.memcmp_if_unknown
return (lookup =~ /\*$/) ? [@fallback, '&'] : [@fallback, '']
lookup =~ /\*$/ ? [@fallback, '&'] : [@fallback, '']
end end
private ########################### private ###########################
def map_C_types def map_c_types
c_types = {} c_types = {}
@config.treat_as.each_pair do |ctype, expecttype| @config.treat_as.each_pair do |ctype, expecttype|
c_type = ctype.gsub(/\s+/,'_') c_type = ctype.gsub(/\s+/, '_')
if (expecttype =~ /\*/) if expecttype =~ /\*/
c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype.gsub(/\*/,'')}_ARRAY" c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype.delete('*')}_ARRAY"
else else
c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype}" c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype}"
c_types[c_type+'*'] ||= "UNITY_TEST_ASSERT_EQUAL_#{expecttype}_ARRAY" c_types[c_type + '*'] ||= "UNITY_TEST_ASSERT_EQUAL_#{expecttype}_ARRAY"
end end
end end
c_types c_types
end end
def import_source def import_source
source = @config.load_unity_helper source = @config.load_unity_helper
return {} if source.nil? return {} if source.nil?
c_types = {} c_types = {}
source = source.gsub(/\/\/.*$/, '') #remove line comments source = source.gsub(/\/\/.*$/, '') # remove line comments
source = source.gsub(/\/\*.*?\*\//m, '') #remove block comments source = source.gsub(/\/\*.*?\*\//m, '') # remove block comments
#scan for comparison helpers # scan for comparison helpers
match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+))\s*\(' + Array.new(4,'\s*\w+\s*').join(',') + '\)') match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+))\s*\(' + Array.new(4, '\s*\w+\s*').join(',') + '\)')
pairs = source.scan(match_regex).flatten.compact pairs = source.scan(match_regex).flatten.compact
(pairs.size/2).times do |i| (pairs.size / 2).times do |i|
expect = pairs[i*2] expect = pairs[i * 2]
ctype = pairs[(i*2)+1] ctype = pairs[(i * 2) + 1]
c_types[ctype] = expect unless expect.include?("_ARRAY") c_types[ctype] = expect unless expect.include?('_ARRAY')
end end
#scan for array variants of those helpers # scan for array variants of those helpers
match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+_ARRAY))\s*\(' + Array.new(5,'\s*\w+\s*').join(',') + '\)') match_regex = Regexp.new('^\s*#define\s+(UNITY_TEST_ASSERT_EQUAL_(\w+_ARRAY))\s*\(' + Array.new(5, '\s*\w+\s*').join(',') + '\)')
pairs = source.scan(match_regex).flatten.compact pairs = source.scan(match_regex).flatten.compact
(pairs.size/2).times do |i| (pairs.size / 2).times do |i|
expect = pairs[i*2] expect = pairs[i * 2]
ctype = pairs[(i*2)+1] ctype = pairs[(i * 2) + 1]
c_types[ctype.gsub('_ARRAY','*')] = expect c_types[ctype.gsub('_ARRAY', '*')] = expect
end end
c_types c_types
end end
end end

View File

@ -1,2 +0,0 @@
217

View File

@ -1,2 +0,0 @@
2.4.6

View File

@ -6,7 +6,7 @@
#include "cmock.h" #include "cmock.h"
//public constants to be used by mocks /* public constants to be used by mocks */
const char* CMockStringOutOfMemory = "CMock has run out of memory. Please allocate more."; const char* CMockStringOutOfMemory = "CMock has run out of memory. Please allocate more.";
const char* CMockStringCalledMore = "Called more times than expected."; const char* CMockStringCalledMore = "Called more times than expected.";
const char* CMockStringCalledLess = "Called fewer times than expected."; const char* CMockStringCalledLess = "Called fewer times than expected.";
@ -19,7 +19,7 @@ const char* CMockStringPtrIsNULL = "Pointer is NULL.";
const char* CMockStringExpNULL = "Expected NULL."; const char* CMockStringExpNULL = "Expected NULL.";
const char* CMockStringMismatch = "Function called with unexpected argument value."; const char* CMockStringMismatch = "Function called with unexpected argument value.";
//private variables /* private variables */
#ifdef CMOCK_MEM_DYNAMIC #ifdef CMOCK_MEM_DYNAMIC
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;
@ -30,37 +30,37 @@ static CMOCK_MEM_INDEX_TYPE CMock_Guts_BufferSize = CMOCK_MEM_SIZE + CMOCK_MEM
static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; static CMOCK_MEM_INDEX_TYPE CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE;
#endif #endif
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemNew * CMock_Guts_MemNew
//------------------------------------------------------- *-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size) CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size)
{ {
CMOCK_MEM_INDEX_TYPE index; CMOCK_MEM_INDEX_TYPE index;
//verify arguments valid (we must be allocating space for at least 1 byte, and the existing chain must be in memory somewhere) /* verify arguments valid (we must be allocating space for at least 1 byte, and the existing chain must be in memory somewhere) */
if (size < 1) if (size < 1)
return CMOCK_GUTS_NONE; return CMOCK_GUTS_NONE;
//verify we have enough room /* verify we have enough room */
size = size + CMOCK_MEM_INDEX_SIZE; size = size + CMOCK_MEM_INDEX_SIZE;
if (size & CMOCK_MEM_ALIGN_MASK) if (size & CMOCK_MEM_ALIGN_MASK)
size = (size + CMOCK_MEM_ALIGN_MASK) & ~CMOCK_MEM_ALIGN_MASK; size = (size + CMOCK_MEM_ALIGN_MASK) & ~CMOCK_MEM_ALIGN_MASK;
if ((CMock_Guts_BufferSize - CMock_Guts_FreePtr) < size) if ((CMock_Guts_BufferSize - CMock_Guts_FreePtr) < size)
{ {
#ifndef CMOCK_MEM_DYNAMIC #ifndef CMOCK_MEM_DYNAMIC
return CMOCK_GUTS_NONE; // nothing we can do; our static buffer is out of memory return CMOCK_GUTS_NONE; /* nothing we can do; our static buffer is out of memory */
#else #else
// our dynamic buffer does not have enough room; request more via realloc() /* our dynamic buffer does not have enough room; request more via realloc() */
CMOCK_MEM_INDEX_TYPE new_buffersize = CMock_Guts_BufferSize + CMOCK_MEM_SIZE + size; CMOCK_MEM_INDEX_TYPE new_buffersize = CMock_Guts_BufferSize + CMOCK_MEM_SIZE + size;
unsigned char* new_buffer = realloc(CMock_Guts_Buffer, (size_t)new_buffersize); unsigned char* new_buffer = realloc(CMock_Guts_Buffer, (size_t)new_buffersize);
if (new_buffer == NULL) if (new_buffer == NULL)
return CMOCK_GUTS_NONE; // realloc() failed; out of memory return CMOCK_GUTS_NONE; /* realloc() failed; out of memory */
CMock_Guts_Buffer = new_buffer; CMock_Guts_Buffer = new_buffer;
CMock_Guts_BufferSize = new_buffersize; CMock_Guts_BufferSize = new_buffersize;
#endif #endif
} }
//determine where we're putting this new block, and init its pointer to be the end of the line /* determine where we're putting this new block, and init its pointer to be the end of the line */
index = CMock_Guts_FreePtr + CMOCK_MEM_INDEX_SIZE; index = CMock_Guts_FreePtr + CMOCK_MEM_INDEX_SIZE;
*(CMOCK_MEM_INDEX_TYPE*)(&CMock_Guts_Buffer[CMock_Guts_FreePtr]) = CMOCK_GUTS_NONE; *(CMOCK_MEM_INDEX_TYPE*)(&CMock_Guts_Buffer[CMock_Guts_FreePtr]) = CMOCK_GUTS_NONE;
CMock_Guts_FreePtr += size; CMock_Guts_FreePtr += size;
@ -68,9 +68,9 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size)
return index; return index;
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemChain * CMock_Guts_MemChain
//------------------------------------------------------- *-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index) CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index)
{ {
CMOCK_MEM_INDEX_TYPE index; CMOCK_MEM_INDEX_TYPE index;
@ -80,12 +80,12 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_
if (root_index == CMOCK_GUTS_NONE) if (root_index == CMOCK_GUTS_NONE)
{ {
//if there is no root currently, we return this object as the root of the chain /* if there is no root currently, we return this object as the root of the chain */
return obj_index; return obj_index;
} }
else else
{ {
//reject illegal nodes /* reject illegal nodes */
if ((root_index < CMOCK_MEM_ALIGN_SIZE) || (root_index >= CMock_Guts_FreePtr)) if ((root_index < CMOCK_MEM_ALIGN_SIZE) || (root_index >= CMock_Guts_FreePtr))
{ {
return CMOCK_GUTS_NONE; return CMOCK_GUTS_NONE;
@ -98,7 +98,7 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_
root = (void*)(&CMock_Guts_Buffer[root_index]); root = (void*)(&CMock_Guts_Buffer[root_index]);
obj = (void*)(&CMock_Guts_Buffer[obj_index]); obj = (void*)(&CMock_Guts_Buffer[obj_index]);
//find the end of the existing chain and add us /* find the end of the existing chain and add us */
next = root; next = root;
do { do {
index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)next - CMOCK_MEM_INDEX_SIZE); index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)next - CMOCK_MEM_INDEX_SIZE);
@ -112,21 +112,21 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_
} }
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemNext * CMock_Guts_MemNext
//------------------------------------------------------- *-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index) CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index)
{ {
CMOCK_MEM_INDEX_TYPE index; CMOCK_MEM_INDEX_TYPE index;
void* previous_item; void* previous_item;
//There is nothing "next" if the pointer isn't from our buffer /* There is nothing "next" if the pointer isn't from our buffer */
if ((previous_item_index < CMOCK_MEM_ALIGN_SIZE) || (previous_item_index >= CMock_Guts_FreePtr)) if ((previous_item_index < CMOCK_MEM_ALIGN_SIZE) || (previous_item_index >= CMock_Guts_FreePtr))
return CMOCK_GUTS_NONE; return CMOCK_GUTS_NONE;
previous_item = (void*)(&CMock_Guts_Buffer[previous_item_index]); previous_item = (void*)(&CMock_Guts_Buffer[previous_item_index]);
//if the pointer is good, then use it to look up the next index /* if the pointer is good, then use it to look up the next index
//(we know the first element always goes in zero, so NEXT must always be > 1) * (we know the first element always goes in zero, so NEXT must always be > 1) */
index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)previous_item - CMOCK_MEM_INDEX_SIZE); index = *(CMOCK_MEM_INDEX_TYPE*)((CMOCK_MEM_PTR_AS_INT)previous_item - CMOCK_MEM_INDEX_SIZE);
if ((index > 1) && (index < CMock_Guts_FreePtr)) if ((index > 1) && (index < CMock_Guts_FreePtr))
return index; return index;
@ -134,9 +134,9 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index
return CMOCK_GUTS_NONE; return CMOCK_GUTS_NONE;
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemEndOfChain * CMock_Guts_MemEndOfChain
//------------------------------------------------------- *-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index) CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index)
{ {
CMOCK_MEM_INDEX_TYPE index = root_index; CMOCK_MEM_INDEX_TYPE index = root_index;
@ -152,9 +152,9 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index)
return index; return index;
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_GetAddressFor * CMock_GetAddressFor
//------------------------------------------------------- *-------------------------------------------------------*/
void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index) void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index)
{ {
if ((index >= CMOCK_MEM_ALIGN_SIZE) && (index < CMock_Guts_FreePtr)) if ((index >= CMOCK_MEM_ALIGN_SIZE) && (index < CMock_Guts_FreePtr))
@ -167,41 +167,41 @@ void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index)
} }
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemBytesCapacity * CMock_Guts_MemBytesCapacity
//------------------------------------------------------- *-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void) CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void)
{ {
return (sizeof(CMock_Guts_Buffer) - CMOCK_MEM_ALIGN_SIZE); return (sizeof(CMock_Guts_Buffer) - CMOCK_MEM_ALIGN_SIZE);
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemBytesFree * CMock_Guts_MemBytesFree
//------------------------------------------------------- *-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void) CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void)
{ {
return CMock_Guts_BufferSize - CMock_Guts_FreePtr; return CMock_Guts_BufferSize - CMock_Guts_FreePtr;
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemBytesUsed * CMock_Guts_MemBytesUsed
//------------------------------------------------------- *-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void) CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void)
{ {
return CMock_Guts_FreePtr - CMOCK_MEM_ALIGN_SIZE; return CMock_Guts_FreePtr - CMOCK_MEM_ALIGN_SIZE;
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemFreeAll * CMock_Guts_MemFreeAll
//------------------------------------------------------- *-------------------------------------------------------*/
void CMock_Guts_MemFreeAll(void) void CMock_Guts_MemFreeAll(void)
{ {
CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; //skip the very beginning CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; /* skip the very beginning */
} }
//------------------------------------------------------- /*-------------------------------------------------------
// CMock_Guts_MemFreeFinal * CMock_Guts_MemFreeFinal
//------------------------------------------------------- *-------------------------------------------------------*/
void CMock_Guts_MemFreeFinal(void) void CMock_Guts_MemFreeFinal(void)
{ {
CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE; CMock_Guts_FreePtr = CMOCK_MEM_ALIGN_SIZE;

View File

@ -11,30 +11,37 @@
#define CMOCK_VERSION_MAJOR 2 #define CMOCK_VERSION_MAJOR 2
#define CMOCK_VERSION_MINOR 5 #define CMOCK_VERSION_MINOR 5
#define CMOCK_VERSION_BUILD 0 #define CMOCK_VERSION_BUILD 4
#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 */
#ifndef CMOCK_MEM_INDEX_TYPE #ifndef CMOCK_MEM_INDEX_TYPE
#define CMOCK_MEM_INDEX_TYPE unsigned int #include <stddef.h>
#define CMOCK_MEM_INDEX_TYPE size_t
#endif #endif
#define CMOCK_GUTS_NONE (0) #define CMOCK_GUTS_NONE (0)
//------------------------------------------------------- #if defined __GNUC__
// Memory API # define CMOCK_FUNCTION_ATTR(a) __attribute__((a))
//------------------------------------------------------- #else
# define CMOCK_FUNCTION_ATTR(a) /* ignore */
#endif
/*-------------------------------------------------------
* Memory API
*-------------------------------------------------------*/
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNew(CMOCK_MEM_INDEX_TYPE size);
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemChain(CMOCK_MEM_INDEX_TYPE root_index, CMOCK_MEM_INDEX_TYPE obj_index);
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemNext(CMOCK_MEM_INDEX_TYPE previous_item_index) CMOCK_FUNCTION_ATTR(pure);
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemEndOfChain(CMOCK_MEM_INDEX_TYPE root_index) CMOCK_FUNCTION_ATTR(pure);
void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index); void* CMock_Guts_GetAddressFor(CMOCK_MEM_INDEX_TYPE index) CMOCK_FUNCTION_ATTR(pure);
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesCapacity(void) CMOCK_FUNCTION_ATTR(const);
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void) CMOCK_FUNCTION_ATTR(pure);
CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void) CMOCK_FUNCTION_ATTR(pure);
void CMock_Guts_MemFreeAll(void); void CMock_Guts_MemFreeAll(void);
void CMock_Guts_MemFreeFinal(void); void CMock_Guts_MemFreeFinal(void);
#endif //CMOCK_FRAMEWORK #endif /* end of CMOCK_FRAMEWORK_H */

View File

@ -9,7 +9,7 @@
#include "unity.h" #include "unity.h"
//These are constants that the generated mocks have access to /* These are constants that the generated mocks have access to */
extern const char* CMockStringOutOfMemory; extern const char* CMockStringOutOfMemory;
extern const char* CMockStringCalledMore; extern const char* CMockStringCalledMore;
extern const char* CMockStringCalledLess; extern const char* CMockStringCalledLess;
@ -22,8 +22,8 @@ extern const char* CMockStringPtrIsNULL;
extern const char* CMockStringExpNULL; extern const char* CMockStringExpNULL;
extern const char* CMockStringMismatch; extern const char* CMockStringMismatch;
//define CMOCK_MEM_DYNAMIC to grab memory as needed with malloc /* define CMOCK_MEM_DYNAMIC to grab memory as needed with malloc
//when you do that, CMOCK_MEM_SIZE is used for incremental size instead of total * when you do that, CMOCK_MEM_SIZE is used for incremental size instead of total */
#ifdef CMOCK_MEM_STATIC #ifdef CMOCK_MEM_STATIC
#undef CMOCK_MEM_DYNAMIC #undef CMOCK_MEM_DYNAMIC
#endif #endif
@ -32,7 +32,7 @@ extern const char* CMockStringMismatch;
#include <stdlib.h> #include <stdlib.h>
#endif #endif
//this is used internally during pointer arithmetic. make sure this type is the same size as the target's pointer type /* this is used internally during pointer arithmetic. make sure this type is the same size as the target's pointer type */
#ifndef CMOCK_MEM_PTR_AS_INT #ifndef CMOCK_MEM_PTR_AS_INT
#ifdef UNITY_POINTER_WIDTH #ifdef UNITY_POINTER_WIDTH
#ifdef UNITY_INT_WIDTH #ifdef UNITY_INT_WIDTH
@ -60,7 +60,7 @@ extern const char* CMockStringMismatch;
#define CMOCK_MEM_PTR_AS_INT unsigned long #define CMOCK_MEM_PTR_AS_INT unsigned long
#endif #endif
//0 for no alignment, 1 for 16-bit, 2 for 32-bit, 3 for 64-bit /* 0 for no alignment, 1 for 16-bit, 2 for 32-bit, 3 for 64-bit */
#ifndef CMOCK_MEM_ALIGN #ifndef CMOCK_MEM_ALIGN
#ifdef UNITY_LONG_WIDTH #ifdef UNITY_LONG_WIDTH
#if (UNITY_LONG_WIDTH == 16) #if (UNITY_LONG_WIDTH == 16)
@ -77,15 +77,15 @@ extern const char* CMockStringMismatch;
#endif #endif
#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 */
#ifndef CMOCK_MEM_SIZE #ifndef CMOCK_MEM_SIZE
#define CMOCK_MEM_SIZE (32768) #define CMOCK_MEM_SIZE (32768)
#endif #endif
//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)
#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)
#endif //CMOCK_FRAMEWORK_INTERNALS #endif /* end of CMOCK_FRAMEWORK_INTERNALS_H */

View File

@ -1,17 +1,12 @@
################################################################################### #
# # # build script written by : Michael Brockus.
# NAME: meson.build # # github repo author: Mike Karlesky, Mark VanderVoord, Greg Williams.
# # #
# AUTHOR: Mike Karlesky, Mark VanderVoord, Greg Williams. # # license: MIT
# WRITTEN BY: Michael Brockus. # #
# #
# License: MIT #
# #
###################################################################################
cmock_dir = include_directories('.') cmock_dir = include_directories('.')
cmock_lib = static_library(meson.project_name(), cmock_lib = static_library(meson.project_name(),
sources: ['cmock.c'], files('cmock.c'),
dependencies: [unity_dep], dependencies: [unity_dep],
include_directories: cmock_dir) include_directories: cmock_dir)

View File

@ -1,211 +0,0 @@
module DeepMerge
MAJOR_VERSION = 0
MINOR_VERSION = 1
FIX_VERSION = 0
VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}.#{FIX_VERSION}"
class InvalidParameter < StandardError; end
DEFAULT_FIELD_KNOCKOUT_PREFIX = '--'
module DeepMergeHash
# ko_hash_merge! will merge and knockout elements prefixed with DEFAULT_FIELD_KNOCKOUT_PREFIX
def ko_deep_merge!(source, options = {})
default_opts = {:knockout_prefix => "--", :preserve_unmergeables => false}
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
end
# deep_merge! will merge and overwrite any unmergeables in destination hash
def deep_merge!(source, options = {})
default_opts = {:preserve_unmergeables => false}
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
end
# deep_merge will merge and skip any unmergeables in destination hash
def deep_merge(source, options = {})
default_opts = {:preserve_unmergeables => true}
DeepMerge::deep_merge!(source, self, default_opts.merge(options))
end
end # DeepMergeHashExt
# Deep Merge core documentation.
# deep_merge! method permits merging of arbitrary child elements. The two top level
# elements must be hashes. These hashes can contain unlimited (to stack limit) levels
# of child elements. These child elements to not have to be of the same types.
# Where child elements are of the same type, deep_merge will attempt to merge them together.
# Where child elements are not of the same type, deep_merge will skip or optionally overwrite
# the destination element with the contents of the source element at that level.
# So if you have two hashes like this:
# source = {:x => [1,2,3], :y => 2}
# dest = {:x => [4,5,'6'], :y => [7,8,9]}
# dest.deep_merge!(source)
# Results: {:x => [1,2,3,4,5,'6'], :y => 2}
# By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
# To avoid this, use "deep_merge" (no bang/exclamation mark)
#
# Options:
# Options are specified in the last parameter passed, which should be in hash format:
# hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
# :preserve_unmergeables DEFAULT: false
# Set to true to skip any unmergeable elements from source
# :knockout_prefix DEFAULT: nil
# Set to string value to signify prefix which deletes elements from existing element
# :sort_merged_arrays DEFAULT: false
# Set to true to sort all arrays that are merged together
# :unpack_arrays DEFAULT: nil
# Set to string value to run "Array::join" then "String::split" against all arrays
# :merge_debug DEFAULT: false
# Set to true to get console output of merge process for debugging
#
# Selected Options Details:
# :knockout_prefix => The purpose of this is to provide a way to remove elements
# from existing Hash by specifying them in a special way in incoming hash
# source = {:x => ['--1', '2']}
# dest = {:x => ['1', '3']}
# dest.ko_deep_merge!(source)
# Results: {:x => ['2','3']}
# Additionally, if the knockout_prefix is passed alone as a string, it will cause
# the entire element to be removed:
# source = {:x => '--'}
# dest = {:x => [1,2,3]}
# dest.ko_deep_merge!(source)
# Results: {:x => ""}
# :unpack_arrays => The purpose of this is to permit compound elements to be passed
# in as strings and to be converted into discrete array elements
# irsource = {:x => ['1,2,3', '4']}
# dest = {:x => ['5','6','7,8']}
# dest.deep_merge!(source, {:unpack_arrays => ','})
# Results: {:x => ['1','2','3','4','5','6','7','8'}
# Why: If receiving data from an HTML form, this makes it easy for a checkbox
# to pass multiple values from within a single HTML element
#
# There are many tests for this library - and you can learn more about the features
# and usages of deep_merge! by just browsing the test examples
def DeepMerge.deep_merge!(source, dest, options = {})
# turn on this line for stdout debugging text
merge_debug = options[:merge_debug] || false
overwrite_unmergeable = !options[:preserve_unmergeables]
knockout_prefix = options[:knockout_prefix] || nil
if knockout_prefix == "" then raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!"; end
if knockout_prefix && !overwrite_unmergeable then raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!"; end
# if present: we will split and join arrays on this char before merging
array_split_char = options[:unpack_arrays] || false
# request that we sort together any arrays when they are merged
sort_merged_arrays = options[:sort_merged_arrays] || false
di = options[:debug_indent] || ''
# do nothing if source is nil
if source.nil? || (source.respond_to?(:blank?) && source.blank?) then return dest; end
# if dest doesn't exist, then simply copy source to it
if dest.nil? && overwrite_unmergeable then dest = source; return dest; end
puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
if source.kind_of?(Hash)
puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
source.each do |src_key, src_value|
if dest.kind_of?(Hash)
puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
if not dest[src_key].nil?
puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' '))
else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
# note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
begin
src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
rescue TypeError
src_dup = src_value
end
dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' '))
end
else # dest isn't a hash, so we overwrite it completely (if permitted)
if overwrite_unmergeable
puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug
dest = overwrite_unmergeables(source, dest, options)
end
end
end
elsif source.kind_of?(Array)
puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
# if we are instructed, join/split any source arrays before processing
if array_split_char
puts "#{di} split/join on source: #{source.inspect}" if merge_debug
source = source.join(array_split_char).split(array_split_char)
if dest.kind_of?(Array) then dest = dest.join(array_split_char).split(array_split_char); end
end
# if there's a naked knockout_prefix in source, that means we are to truncate dest
if source.index(knockout_prefix) then dest = clear_or_nil(dest); source.delete(knockout_prefix); end
if dest.kind_of?(Array)
if knockout_prefix
print "#{di} knocking out: " if merge_debug
# remove knockout prefix items from both source and dest
source.delete_if do |ko_item|
retval = false
item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
if item != ko_item
print "#{ko_item} - " if merge_debug
dest.delete(item)
dest.delete(ko_item)
retval = true
end
retval
end
puts if merge_debug
end
puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
dest = dest | source
if sort_merged_arrays then dest.sort!; end
elsif overwrite_unmergeable
puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
dest = overwrite_unmergeables(source, dest, options)
end
else # src_hash is not an array or hash, so we'll have to overwrite dest
puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
dest = overwrite_unmergeables(source, dest, options)
end
puts "#{di}Returning #{dest.inspect}" if merge_debug
dest
end # deep_merge!
# allows deep_merge! to uniformly handle overwriting of unmergeable entities
def DeepMerge::overwrite_unmergeables(source, dest, options)
merge_debug = options[:merge_debug] || false
overwrite_unmergeable = !options[:preserve_unmergeables]
knockout_prefix = options[:knockout_prefix] || false
di = options[:debug_indent] || ''
if knockout_prefix && overwrite_unmergeable
if source.kind_of?(String) # remove knockout string from source before overwriting dest
src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
else
src_tmp = source
end
if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
dest = src_tmp
else # if we do find a knockout_prefix, then we just delete dest
puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
dest = ""
end
elsif overwrite_unmergeable
dest = source
end
dest
end
def DeepMerge::clear_or_nil(obj)
if obj.respond_to?(:clear)
obj.clear
else
obj = nil
end
obj
end
end # module DeepMerge
class Hash
include DeepMerge::DeepMergeHash
end

View File

@ -13,7 +13,9 @@ require 'fileutils'
require 'pathname' require 'pathname'
# TEMPLATE_TST # TEMPLATE_TST
TEMPLATE_TST ||= '#include "unity.h" TEMPLATE_TST ||= '#ifdef TEST
#include "unity.h"
%2$s#include "%1$s.h" %2$s#include "%1$s.h"
@ -25,10 +27,12 @@ void tearDown(void)
{ {
} }
void test_%1$s_NeedToImplement(void) void test_%4$s_NeedToImplement(void)
{ {
TEST_IGNORE_MESSAGE("Need to Implement %1$s"); TEST_IGNORE_MESSAGE("Need to Implement %1$s");
} }
#endif // TEST
'.freeze '.freeze
# TEMPLATE_SRC # TEMPLATE_SRC
@ -163,24 +167,23 @@ class UnityModuleGenerator
files files
end end
############################
def neutralize_filename(name, start_cap = true)
return name if name.empty?
name = name.split(/(?:\s+|_|(?=[A-Z][a-z]))|(?<=[a-z])(?=[A-Z])/).map { |v| v.capitalize }.join('_')
name = name[0].downcase + name[1..-1] unless start_cap
return name
end
############################ ############################
def create_filename(part1, part2 = '') def create_filename(part1, part2 = '')
if part2.empty? name = part2.empty? ? part1 : part1 + '_' + part2
case (@options[:naming]) case (@options[:naming])
when 'bumpy' then part1 when 'bumpy' then neutralize_filename(name,false).delete('_')
when 'camel' then part1 when 'camel' then neutralize_filename(name).delete('_')
when 'snake' then part1.downcase when 'snake' then neutralize_filename(name).downcase
when 'caps' then part1.upcase when 'caps' then neutralize_filename(name).upcase
else part1 else name
end
else
case (@options[:naming])
when 'bumpy' then part1 + part2
when 'camel' then part1 + part2
when 'snake' then part1.downcase + '_' + part2.downcase
when 'caps' then part1.upcase + '_' + part2.upcase
else part1 + '_' + part2
end
end end
end end
@ -208,7 +211,8 @@ class UnityModuleGenerator
f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil? f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
f.write(file[:template] % [file[:name], f.write(file[:template] % [file[:name],
file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join, file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join,
file[:name].upcase]) file[:name].upcase.gsub(/-/, '_'),
file[:name].gsub(/-/, '_')])
end end
if @options[:update_svn] if @options[:update_svn]
`svn add \"#{file[:path]}\"` `svn add \"#{file[:path]}\"`

View File

@ -42,7 +42,9 @@ class UnityTestRunnerGenerator
main_export_decl: '', main_export_decl: '',
cmdline_args: false, cmdline_args: false,
omit_begin_end: false, omit_begin_end: false,
use_param_tests: false use_param_tests: false,
include_extensions: '(?:hpp|hh|H|h)',
source_extensions: '(?:cpp|cc|ino|C|c)'
} }
end end
@ -92,7 +94,7 @@ class UnityTestRunnerGenerator
create_suite_setup(output) create_suite_setup(output)
create_suite_teardown(output) create_suite_teardown(output)
create_reset(output) create_reset(output)
create_run_test(output) create_run_test(output) unless tests.empty?
create_args_wrappers(output, tests) create_args_wrappers(output, tests)
create_main(output, input_file, tests, used_mocks) create_main(output, input_file, tests, used_mocks)
end end
@ -108,7 +110,7 @@ class UnityTestRunnerGenerator
tests_and_line_numbers = [] tests_and_line_numbers = []
# contains characters which will be substituted from within strings, doing # contains characters which will be substituted from within strings, doing
# this prevents these characters from interferring with scrubbers # this prevents these characters from interfering with scrubbers
# @ is not a valid C character, so there should be no clashes with files genuinely containing these markers # @ is not a valid C character, so there should be no clashes with files genuinely containing these markers
substring_subs = { '{' => '@co@', '}' => '@cc@', ';' => '@ss@', '/' => '@fs@' } substring_subs = { '{' => '@co@', '}' => '@cc@', ';' => '@ss@', '/' => '@fs@' }
substring_re = Regexp.union(substring_subs.keys) substring_re = Regexp.union(substring_subs.keys)
@ -128,7 +130,7 @@ class UnityTestRunnerGenerator
lines.each_with_index do |line, _index| lines.each_with_index do |line, _index|
# find tests # find tests
next unless line =~ /^((?:\s*TEST_CASE\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m next unless line =~ /^((?:\s*(?:TEST_CASE|TEST_RANGE)\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m
arguments = Regexp.last_match(1) arguments = Regexp.last_match(1)
name = Regexp.last_match(2) name = Regexp.last_match(2)
@ -139,6 +141,20 @@ class UnityTestRunnerGenerator
if @options[:use_param_tests] && !arguments.empty? if @options[:use_param_tests] && !arguments.empty?
args = [] args = []
arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) { |a| args << a[0] } arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) { |a| args << a[0] }
arguments.scan(/\s*TEST_RANGE\s*\((.*)\)\s*$/).flatten.each do |range_str|
args += range_str.scan(/\[\s*(-?\d+.?\d*),\s*(-?\d+.?\d*),\s*(-?\d+.?\d*)\s*\]/).map do |arg_values_str|
arg_values_str.map do |arg_value_str|
arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i
end
end.map do |arg_values|
(arg_values[0]..arg_values[1]).step(arg_values[2]).to_a
end.reduce do |result, arg_range_expanded|
result.product(arg_range_expanded)
end.map do |arg_combinations|
arg_combinations.flatten.join(', ')
end
end
end end
tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 } tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 }
@ -170,9 +186,9 @@ class UnityTestRunnerGenerator
# parse out includes # parse out includes
includes = { includes = {
local: source.scan(/^\s*#include\s+\"\s*(.+)\.[hH]\s*\"/).flatten, local: source.scan(/^\s*#include\s+\"\s*(.+\.#{@options[:include_extensions]})\s*\"/).flatten,
system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }, system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" },
linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+)\.[cC]\w*\s*\"/).flatten linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+\.#{@options[:source_extensions]})\s*\"/).flatten
} }
includes includes
end end
@ -181,7 +197,7 @@ class UnityTestRunnerGenerator
mock_headers = [] mock_headers = []
includes.each do |include_path| includes.each do |include_path|
include_file = File.basename(include_path) include_file = File.basename(include_path)
mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}$/i mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}\.h$/i
end end
mock_headers mock_headers
end end
@ -190,7 +206,7 @@ class UnityTestRunnerGenerator
@options[:has_setup] = source =~ /void\s+#{@options[:setup_name]}\s*\(/ @options[:has_setup] = source =~ /void\s+#{@options[:setup_name]}\s*\(/
@options[:has_teardown] = source =~ /void\s+#{@options[:teardown_name]}\s*\(/ @options[:has_teardown] = source =~ /void\s+#{@options[:teardown_name]}\s*\(/
@options[:has_suite_setup] ||= (source =~ /void\s+suiteSetUp\s*\(/) @options[:has_suite_setup] ||= (source =~ /void\s+suiteSetUp\s*\(/)
@options[:has_suite_teardown] ||= (source =~ /void\s+suiteTearDown\s*\(/) @options[:has_suite_teardown] ||= (source =~ /int\s+suiteTearDown\s*\(int\s+([a-zA-Z0-9_])+\s*\)/)
end end
def create_header(output, mocks, testfile_includes = []) def create_header(output, mocks, testfile_includes = [])
@ -205,14 +221,14 @@ class UnityTestRunnerGenerator
output.puts("#include \"#{File.basename(@options[:header_file])}\"") output.puts("#include \"#{File.basename(@options[:header_file])}\"")
else else
@options[:includes].flatten.uniq.compact.each do |inc| @options[:includes].flatten.uniq.compact.each do |inc|
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
end end
testfile_includes.each do |inc| testfile_includes.each do |inc|
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
end end
end end
mocks.each do |mock| mocks.each do |mock|
output.puts("#include \"#{mock.gsub('.h', '')}.h\"") output.puts("#include \"#{mock}\"")
end end
output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception) output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception)
@ -247,7 +263,7 @@ class UnityTestRunnerGenerator
output.puts(' GlobalOrderError = NULL;') output.puts(' GlobalOrderError = NULL;')
end end
mocks = mock_headers.map { |mock| File.basename(mock) } mocks = mock_headers.map { |mock| File.basename(mock, '.*') }
mocks.each do |mock| mocks.each do |mock|
mock_clean = TypeSanitizer.sanitize_c_identifier(mock) mock_clean = TypeSanitizer.sanitize_c_identifier(mock)
output.puts(" #{mock_clean}_Init();") output.puts(" #{mock_clean}_Init();")
@ -325,8 +341,8 @@ class UnityTestRunnerGenerator
def create_run_test(output) def create_run_test(output)
require 'erb' require 'erb'
template = ERB.new(File.read(File.join(__dir__, 'run_test.erb'))) template = ERB.new(File.read(File.join(__dir__, 'run_test.erb')), nil, '<>')
output.puts(template.result(binding)) output.puts("\n" + template.result(binding))
end end
def create_args_wrappers(output, tests) def create_args_wrappers(output, tests)
@ -346,7 +362,7 @@ class UnityTestRunnerGenerator
end end
def create_main(output, filename, tests, used_mocks) def create_main(output, filename, tests, used_mocks)
output.puts("\n\n/*=======MAIN=====*/") output.puts("\n/*=======MAIN=====*/")
main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s
if @options[:cmdline_args] if @options[:cmdline_args]
if main_name != 'main' if main_name != 'main'
@ -410,7 +426,7 @@ class UnityTestRunnerGenerator
output.puts(' return suiteTearDown(UnityEnd());') output.puts(' return suiteTearDown(UnityEnd());')
end end
else else
output.puts(' return UnityEnd();') if not @options[:omit_begin_end] output.puts(' return UnityEnd();') unless @options[:omit_begin_end]
end end
output.puts('}') output.puts('}')
end end
@ -423,10 +439,10 @@ class UnityTestRunnerGenerator
output.puts("#include \"#{@options[:framework]}.h\"") output.puts("#include \"#{@options[:framework]}.h\"")
output.puts('#include "cmock.h"') unless used_mocks.empty? output.puts('#include "cmock.h"') unless used_mocks.empty?
@options[:includes].flatten.uniq.compact.each do |inc| @options[:includes].flatten.uniq.compact.each do |inc|
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
end end
testfile_includes.each do |inc| testfile_includes.each do |inc|
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
end end
output.puts "\n" output.puts "\n"
tests.each do |test| tests.each do |test|
@ -449,13 +465,13 @@ if $0 == __FILE__
when '-cexception' when '-cexception'
options[:plugins] = [:cexception] options[:plugins] = [:cexception]
true true
when /\.*\.ya?ml/ when /\.*\.ya?ml$/
options = UnityTestRunnerGenerator.grab_config(arg) options = UnityTestRunnerGenerator.grab_config(arg)
true true
when /--(\w+)=\"?(.*)\"?/ when /--(\w+)=\"?(.*)\"?/
options[Regexp.last_match(1).to_sym] = Regexp.last_match(2) options[Regexp.last_match(1).to_sym] = Regexp.last_match(2)
true true
when /\.*\.h/ when /\.*\.(?:hpp|hh|H|h)$/
options[:includes] << arg options[:includes] << arg
true true
else false else false

View File

@ -1,5 +1,5 @@
/*=======Test Runner Used To Run Each Test=====*/ /*=======Test Runner Used To Run Each Test=====*/
static void run_test(UnityTestFunction func, const char* name, int line_num) static void run_test(UnityTestFunction func, const char* name, UNITY_LINE_TYPE line_num)
{ {
Unity.CurrentTestName = name; Unity.CurrentTestName = name;
Unity.CurrentTestLineNumber = line_num; Unity.CurrentTestLineNumber = line_num;
@ -16,13 +16,14 @@ static void run_test(UnityTestFunction func, const char* name, int line_num)
<% if @options[:plugins].include?(:cexception) %> <% if @options[:plugins].include?(:cexception) %>
CEXCEPTION_T e; CEXCEPTION_T e;
Try { Try {
<% end %>
<%= @options[:setup_name] %>(); <%= @options[:setup_name] %>();
func(); func();
<% if @options[:plugins].include?(:cexception) %>
} Catch(e) { } Catch(e) {
TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!"); TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!");
} }
<% else %>
<%= @options[:setup_name] %>();
func();
<% end %> <% end %>
} }
if (TEST_PROTECT()) if (TEST_PROTECT())

View File

@ -1,2 +0,0 @@
122

View File

@ -1,2 +0,0 @@
2.4.3

View File

@ -1,22 +0,0 @@
###################################################################################
# #
# NAME: CMakeLists.txt #
# #
# AUTHOR: Mike Karlesky, Mark VanderVoord, Greg Williams. #
# WRITTEN BY: Michael Brockus. #
# #
# License: MIT #
# #
###################################################################################
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
add_library(unity STATIC "unity.c")
install(TARGETS unity EXPORT unityConfig
ARCHIVE DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_INSTALL_BINDIR}"
INCLUDES DESTINATION "${CMAKE_INSTALL_LIBDIR}")

View File

@ -1,16 +1,11 @@
################################################################################### #
# # # build script written by : Michael Brockus.
# NAME: meson.build # # github repo author: Mike Karlesky, Mark VanderVoord, Greg Williams.
# # #
# AUTHOR: Mike Karlesky, Mark VanderVoord, Greg Williams. # # license: MIT
# WRITTEN BY: Michael Brockus. # #
# #
# License: MIT #
# #
###################################################################################
unity_dir = include_directories('.') unity_dir = include_directories('.')
unity_lib = static_library(meson.project_name(), unity_lib = static_library(meson.project_name(),
sources: ['unity.c'], files('unity.c'),
include_directories: unity_dir) include_directories: unity_dir)

View File

@ -1,6 +1,6 @@
/* ========================================================================= /* =========================================================================
Unity Project - A Test Framework for C Unity Project - A Test Framework for C
Copyright (c) 2007-19 Mike Karlesky, Mark VanderVoord, Greg Williams Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
[Released under MIT License. Please refer to license.txt for details] [Released under MIT License. Please refer to license.txt for details]
============================================================================ */ ============================================================================ */
@ -21,7 +21,7 @@ void UNITY_OUTPUT_CHAR(int);
/* Helpful macros for us to use here in Assert functions */ /* Helpful macros for us to use here in Assert functions */
#define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } #define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); }
#define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } #define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); }
#define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) return #define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) TEST_ABORT()
struct UNITY_STORAGE_T Unity; struct UNITY_STORAGE_T Unity;
@ -43,6 +43,7 @@ static const char PROGMEM UnityStrWas[] = " Was ";
static const char PROGMEM UnityStrGt[] = " to be greater than "; static const char PROGMEM UnityStrGt[] = " to be greater than ";
static const char PROGMEM UnityStrLt[] = " to be less than "; static const char PROGMEM UnityStrLt[] = " to be less than ";
static const char PROGMEM UnityStrOrEqual[] = "or equal to "; static const char PROGMEM UnityStrOrEqual[] = "or equal to ";
static const char PROGMEM UnityStrNotEqual[] = " to be not equal to ";
static const char PROGMEM UnityStrElement[] = " Element "; static const char PROGMEM UnityStrElement[] = " Element ";
static const char PROGMEM UnityStrByte[] = " Byte "; static const char PROGMEM UnityStrByte[] = " Byte ";
static const char PROGMEM UnityStrMemory[] = " Memory Mismatch."; static const char PROGMEM UnityStrMemory[] = " Memory Mismatch.";
@ -66,9 +67,10 @@ static const char PROGMEM UnityStrBreaker[] = "------------------
static const char PROGMEM UnityStrResultsTests[] = " Tests "; static const char PROGMEM UnityStrResultsTests[] = " Tests ";
static const char PROGMEM UnityStrResultsFailures[] = " Failures "; static const char PROGMEM UnityStrResultsFailures[] = " Failures ";
static const char PROGMEM UnityStrResultsIgnored[] = " Ignored "; static const char PROGMEM UnityStrResultsIgnored[] = " Ignored ";
#ifndef UNITY_EXCLUDE_DETAILS
static const char PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; static const char PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " ";
static const char PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; static const char PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " ";
#endif
/*----------------------------------------------- /*-----------------------------------------------
* Pretty Printers & Test Result Output Handlers * Pretty Printers & Test Result Output Handlers
*-----------------------------------------------*/ *-----------------------------------------------*/
@ -146,121 +148,6 @@ void UnityPrint(const char* string)
} }
} }
} }
/*-----------------------------------------------*/
#ifdef UNITY_INCLUDE_PRINT_FORMATTED
void UnityPrintFormatted(const char* format, ...)
{
const char* pch = format;
va_list va;
va_start(va, format);
if (pch != NULL)
{
while (*pch)
{
/* format identification character */
if (*pch == '%')
{
pch++;
if (pch != NULL)
{
switch (*pch)
{
case 'd':
case 'i':
{
const int number = va_arg(va, int);
UnityPrintNumber((UNITY_INT)number);
break;
}
#ifndef UNITY_EXCLUDE_FLOAT_PRINT
case 'f':
case 'g':
{
const double number = va_arg(va, double);
UnityPrintFloat((UNITY_DOUBLE)number);
break;
}
#endif
case 'u':
{
const unsigned int number = va_arg(va, unsigned int);
UnityPrintNumberUnsigned((UNITY_UINT)number);
break;
}
case 'b':
{
const unsigned int number = va_arg(va, unsigned int);
const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1;
UNITY_OUTPUT_CHAR('0');
UNITY_OUTPUT_CHAR('b');
UnityPrintMask(mask, (UNITY_UINT)number);
break;
}
case 'x':
case 'X':
case 'p':
{
const unsigned int number = va_arg(va, unsigned int);
UNITY_OUTPUT_CHAR('0');
UNITY_OUTPUT_CHAR('x');
UnityPrintNumberHex((UNITY_UINT)number, 8);
break;
}
case 'c':
{
const int ch = va_arg(va, int);
UnityPrintChar((const char *)&ch);
break;
}
case 's':
{
const char * string = va_arg(va, const char *);
UnityPrint(string);
break;
}
case '%':
{
UnityPrintChar(pch);
break;
}
default:
{
/* print the unknown format character */
UNITY_OUTPUT_CHAR('%');
UnityPrintChar(pch);
break;
}
}
}
}
#ifdef UNITY_OUTPUT_COLOR
/* print ANSI escape code */
else if ((*pch == 27) && (*(pch + 1) == '['))
{
pch += UnityPrintAnsiEscapeString(pch);
continue;
}
#endif
else if (*pch == '\n')
{
UNITY_PRINT_EOL();
}
else
{
UnityPrintChar(pch);
}
pch++;
}
}
va_end(va);
}
#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */
/*-----------------------------------------------*/ /*-----------------------------------------------*/
void UnityPrintLen(const char* string, const UNITY_UINT32 length) void UnityPrintLen(const char* string, const UNITY_UINT32 length)
{ {
@ -558,7 +445,7 @@ void UnityPrintFloat(const UNITY_DOUBLE input_number)
/* build up buffer in reverse order */ /* build up buffer in reverse order */
digits = 0; digits = 0;
while ((n != 0) || (digits < (decimals + 1))) while ((n != 0) || (digits <= decimals))
{ {
buf[digits++] = (char)('0' + n % 10); buf[digits++] = (char)('0' + n % 10);
n /= 10; n /= 10;
@ -680,6 +567,10 @@ static void UnityAddMsgIfSpecified(const char* msg)
if (msg) if (msg)
{ {
UnityPrint(UnityStrSpacer); UnityPrint(UnityStrSpacer);
#ifdef UNITY_PRINT_TEST_CONTEXT
UNITY_PRINT_TEST_CONTEXT();
#endif
#ifndef UNITY_EXCLUDE_DETAILS #ifndef UNITY_EXCLUDE_DETAILS
if (Unity.CurrentDetail1) if (Unity.CurrentDetail1)
{ {
@ -863,9 +754,10 @@ void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold,
UnityTestResultsFailBegin(lineNumber); UnityTestResultsFailBegin(lineNumber);
UnityPrint(UnityStrExpected); UnityPrint(UnityStrExpected);
UnityPrintNumberByStyle(actual, style); UnityPrintNumberByStyle(actual, style);
if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); }
if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); }
UnityPrintNumberByStyle(threshold, style); UnityPrintNumberByStyle(threshold, style);
UnityAddMsgIfSpecified(msg); UnityAddMsgIfSpecified(msg);
UNITY_FAIL_AND_BAIL; UNITY_FAIL_AND_BAIL;
@ -1110,7 +1002,7 @@ void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
is_trait = !isinf(actual) && !isnan(actual); is_trait = !isinf(actual) && !isnan(actual);
break; break;
default: default: /* including UNITY_FLOAT_INVALID_TRAIT */
trait_index = 0; trait_index = 0;
trait_names[0] = UnityStrInvalidFloatTrait; trait_names[0] = UnityStrInvalidFloatTrait;
break; break;
@ -1250,7 +1142,7 @@ void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual,
is_trait = !isinf(actual) && !isnan(actual); is_trait = !isinf(actual) && !isnan(actual);
break; break;
default: default: /* including UNITY_FLOAT_INVALID_TRAIT */
trait_index = 0; trait_index = 0;
trait_names[0] = UnityStrInvalidFloatTrait; trait_names[0] = UnityStrInvalidFloatTrait;
break; break;
@ -1731,6 +1623,133 @@ UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num)
} }
#endif #endif
/*-----------------------------------------------
* printf helper function
*-----------------------------------------------*/
#ifdef UNITY_INCLUDE_PRINT_FORMATTED
static void UnityPrintFVA(const char* format, va_list va)
{
const char* pch = format;
if (pch != NULL)
{
while (*pch)
{
/* format identification character */
if (*pch == '%')
{
pch++;
if (pch != NULL)
{
switch (*pch)
{
case 'd':
case 'i':
{
const int number = va_arg(va, int);
UnityPrintNumber((UNITY_INT)number);
break;
}
#ifndef UNITY_EXCLUDE_FLOAT_PRINT
case 'f':
case 'g':
{
const double number = va_arg(va, double);
UnityPrintFloat((UNITY_DOUBLE)number);
break;
}
#endif
case 'u':
{
const unsigned int number = va_arg(va, unsigned int);
UnityPrintNumberUnsigned((UNITY_UINT)number);
break;
}
case 'b':
{
const unsigned int number = va_arg(va, unsigned int);
const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1;
UNITY_OUTPUT_CHAR('0');
UNITY_OUTPUT_CHAR('b');
UnityPrintMask(mask, (UNITY_UINT)number);
break;
}
case 'x':
case 'X':
case 'p':
{
const unsigned int number = va_arg(va, unsigned int);
UNITY_OUTPUT_CHAR('0');
UNITY_OUTPUT_CHAR('x');
UnityPrintNumberHex((UNITY_UINT)number, 8);
break;
}
case 'c':
{
const int ch = va_arg(va, int);
UnityPrintChar((const char *)&ch);
break;
}
case 's':
{
const char * string = va_arg(va, const char *);
UnityPrint(string);
break;
}
case '%':
{
UnityPrintChar(pch);
break;
}
default:
{
/* print the unknown format character */
UNITY_OUTPUT_CHAR('%');
UnityPrintChar(pch);
break;
}
}
}
}
#ifdef UNITY_OUTPUT_COLOR
/* print ANSI escape code */
else if ((*pch == 27) && (*(pch + 1) == '['))
{
pch += UnityPrintAnsiEscapeString(pch);
continue;
}
#endif
else if (*pch == '\n')
{
UNITY_PRINT_EOL();
}
else
{
UnityPrintChar(pch);
}
pch++;
}
}
}
void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...)
{
UnityTestResultsBegin(Unity.TestFile, line);
UnityPrint("INFO");
if(format != NULL)
{
UnityPrint(": ");
va_list va;
va_start(va, format);
UnityPrintFVA(format, va);
va_end(va);
}
UNITY_PRINT_EOL();
}
#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */
/*----------------------------------------------- /*-----------------------------------------------
* Control Functions * Control Functions
*-----------------------------------------------*/ *-----------------------------------------------*/
@ -1746,6 +1765,9 @@ void UnityFail(const char* msg, const UNITY_LINE_TYPE line)
{ {
UNITY_OUTPUT_CHAR(':'); UNITY_OUTPUT_CHAR(':');
#ifdef UNITY_PRINT_TEST_CONTEXT
UNITY_PRINT_TEST_CONTEXT();
#endif
#ifndef UNITY_EXCLUDE_DETAILS #ifndef UNITY_EXCLUDE_DETAILS
if (Unity.CurrentDetail1) if (Unity.CurrentDetail1)
{ {
@ -1800,6 +1822,8 @@ void UnityMessage(const char* msg, const UNITY_LINE_TYPE line)
} }
/*-----------------------------------------------*/ /*-----------------------------------------------*/
/* If we have not defined our own test runner, then include our default test runner to make life easier */
#ifndef UNITY_SKIP_DEFAULT_RUNNER
void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum)
{ {
Unity.CurrentTestName = FuncName; Unity.CurrentTestName = FuncName;
@ -1819,6 +1843,7 @@ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int
UNITY_EXEC_TIME_STOP(); UNITY_EXEC_TIME_STOP();
UnityConcludeTest(); UnityConcludeTest();
} }
#endif
/*-----------------------------------------------*/ /*-----------------------------------------------*/
void UnitySetTestFile(const char* filename) void UnitySetTestFile(const char* filename)

View File

@ -1,6 +1,6 @@
/* ========================================== /* ==========================================
Unity Project - A Test Framework for C Unity Project - A Test Framework for C
Copyright (c) 2007-19 Mike Karlesky, Mark VanderVoord, Greg Williams Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
[Released under MIT License. Please refer to license.txt for details] [Released under MIT License. Please refer to license.txt for details]
========================================== */ ========================================== */
@ -10,7 +10,7 @@
#define UNITY_VERSION_MAJOR 2 #define UNITY_VERSION_MAJOR 2
#define UNITY_VERSION_MINOR 5 #define UNITY_VERSION_MINOR 5
#define UNITY_VERSION_BUILD 0 #define UNITY_VERSION_BUILD 4
#define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD) #define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD)
#ifdef __cplusplus #ifdef __cplusplus
@ -104,6 +104,9 @@ void verifyTest(void);
#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) #define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL)
#define TEST_MESSAGE(message) UnityMessage((message), __LINE__) #define TEST_MESSAGE(message) UnityMessage((message), __LINE__)
#define TEST_ONLY() #define TEST_ONLY()
#ifdef UNITY_INCLUDE_PRINT_FORMATTED
#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), __VA_ARGS__)
#endif
/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. /* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails.
* This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */
@ -125,6 +128,8 @@ void verifyTest(void);
#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") #define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE")
#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") #define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL")
#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") #define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL")
#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty")
#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty")
/* Integers (of all sizes) */ /* Integers (of all sizes) */
#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
@ -145,10 +150,28 @@ void verifyTest(void);
#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL)
#define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL)
#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL)
#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) #define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, NULL) #define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL)
#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) #define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, NULL) #define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL)
/* Integer Not Equal To (of all sizes) */
#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
/* Integer Greater Than/ Less Than (of all sizes) */ /* Integer Greater Than/ Less Than (of all sizes) */
#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL)
@ -373,6 +396,8 @@ void verifyTest(void);
#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) #define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message))
#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) #define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message))
#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) #define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message))
#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message))
#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message))
/* Integers (of all sizes) */ /* Integers (of all sizes) */
#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message))
@ -398,6 +423,25 @@ void verifyTest(void);
#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) #define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message))
#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message))
/* Integer Not Equal To (of all sizes) */
#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
/* Integer Greater Than/ Less Than (of all sizes) */ /* Integer Greater Than/ Less Than (of all sizes) */
#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))
#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))

View File

@ -1,6 +1,6 @@
/* ========================================== /* ==========================================
Unity Project - A Test Framework for C Unity Project - A Test Framework for C
Copyright (c) 2007-19 Mike Karlesky, Mark VanderVoord, Greg Williams Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
[Released under MIT License. Please refer to license.txt for details] [Released under MIT License. Please refer to license.txt for details]
========================================== */ ========================================== */
@ -40,6 +40,26 @@
#include <limits.h> #include <limits.h>
#endif #endif
#if defined(__GNUC__) || defined(__clang__)
#define UNITY_FUNCTION_ATTR(a) __attribute__((a))
#else
#define UNITY_FUNCTION_ATTR(a) /* ignore */
#endif
#ifndef UNITY_NORETURN
#if defined(__cplusplus)
#if __cplusplus >= 201103L
#define UNITY_NORETURN [[ noreturn ]]
#endif
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#include <stdnoreturn.h>
#define UNITY_NORETURN noreturn
#endif
#endif
#ifndef UNITY_NORETURN
#define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn)
#endif
/*------------------------------------------------------- /*-------------------------------------------------------
* Guess Widths If Not Specified * Guess Widths If Not Specified
*-------------------------------------------------------*/ *-------------------------------------------------------*/
@ -327,7 +347,7 @@ typedef UNITY_FLOAT_TYPE UNITY_FLOAT;
UnityPrintNumberUnsigned(execTimeMs); \ UnityPrintNumberUnsigned(execTimeMs); \
UnityPrint(" ms)"); \ UnityPrint(" ms)"); \
} }
#elif defined(__unix__) #elif defined(__unix__) || defined(__APPLE__)
#include <time.h> #include <time.h>
#define UNITY_TIME_TYPE struct timespec #define UNITY_TIME_TYPE struct timespec
#define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t) #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t)
@ -421,6 +441,7 @@ typedef enum
UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO, UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO,
UNITY_SMALLER_THAN = 0x4, UNITY_SMALLER_THAN = 0x4,
UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO, UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO,
UNITY_NOT_EQUAL = 0x0,
UNITY_UNKNOWN UNITY_UNKNOWN
} UNITY_COMPARISON_T; } UNITY_COMPARISON_T;
@ -479,7 +500,12 @@ void UnityBegin(const char* filename);
int UnityEnd(void); int UnityEnd(void);
void UnitySetTestFile(const char* filename); void UnitySetTestFile(const char* filename);
void UnityConcludeTest(void); void UnityConcludeTest(void);
#ifndef RUN_TEST
void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum);
#else
#define UNITY_SKIP_DEFAULT_RUNNER
#endif
/*------------------------------------------------------- /*-------------------------------------------------------
* Details Support * Details Support
@ -503,6 +529,10 @@ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int
#endif #endif
#endif #endif
#ifdef UNITY_PRINT_TEST_CONTEXT
void UNITY_PRINT_TEST_CONTEXT(void);
#endif
/*------------------------------------------------------- /*-------------------------------------------------------
* Test Output * Test Output
*-------------------------------------------------------*/ *-------------------------------------------------------*/
@ -510,7 +540,7 @@ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int
void UnityPrint(const char* string); void UnityPrint(const char* string);
#ifdef UNITY_INCLUDE_PRINT_FORMATTED #ifdef UNITY_INCLUDE_PRINT_FORMATTED
void UnityPrintFormatted(const char* format, ...); void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...);
#endif #endif
void UnityPrintLen(const char* string, const UNITY_UINT32 length); void UnityPrintLen(const char* string, const UNITY_UINT32 length);
@ -601,8 +631,14 @@ void UnityAssertNumbersArrayWithin(const UNITY_UINT delta,
const UNITY_DISPLAY_STYLE_T style, const UNITY_DISPLAY_STYLE_T style,
const UNITY_FLAGS_T flags); const UNITY_FLAGS_T flags);
#ifndef UNITY_EXCLUDE_SETJMP_H
UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line);
UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
#else
void UnityFail(const char* message, const UNITY_LINE_TYPE line); void UnityFail(const char* message, const UNITY_LINE_TYPE line);
void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
#endif
void UnityMessage(const char* message, const UNITY_LINE_TYPE line); void UnityMessage(const char* message, const UNITY_LINE_TYPE line);
#ifndef UNITY_EXCLUDE_FLOAT #ifndef UNITY_EXCLUDE_FLOAT
@ -691,11 +727,8 @@ extern const char UnityStrErrShorthand[];
#endif #endif
#endif #endif
#ifdef UNITY_SUPPORT_VARIADIC_MACROS #ifdef UNITY_SUPPORT_VARIADIC_MACROS
#define RUN_TEST(...) UnityDefaultTestRun(RUN_TEST_FIRST(__VA_ARGS__), RUN_TEST_SECOND(__VA_ARGS__)) #define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway)
#define RUN_TEST_FIRST(...) RUN_TEST_FIRST_HELPER(__VA_ARGS__, throwaway) #define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line)
#define RUN_TEST_FIRST_HELPER(first, ...) (first), #first
#define RUN_TEST_SECOND(...) RUN_TEST_SECOND_HELPER(__VA_ARGS__, __LINE__, throwaway)
#define RUN_TEST_SECOND_HELPER(first, second, ...) (second)
#endif #endif
#endif #endif
@ -753,9 +786,11 @@ int UnityTestMatches(void);
* Test Asserts * Test Asserts
*-------------------------------------------------------*/ *-------------------------------------------------------*/
#define UNITY_TEST_ASSERT(condition, line, message) if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));} #define UNITY_TEST_ASSERT(condition, line, message) do {if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));}} while(0)
#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) #define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message))
#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) #define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message))
#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (UNITY_LINE_TYPE)(line), (message))
#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (UNITY_LINE_TYPE)(line), (message))
#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
@ -771,6 +806,19 @@ int UnityTestMatches(void);
#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) #define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) #define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line))
#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) #define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
@ -841,7 +889,7 @@ int UnityTestMatches(void);
#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY)
#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY)
#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY)
#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY)
#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY)
#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY)
#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY)
@ -900,6 +948,9 @@ int UnityTestMatches(void);
#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)