mirror of
https://github.com/ThrowTheSwitch/CMock.git
synced 2026-08-05 18:57:50 +00:00
Merge pull request #261 from laurensmiers/master
Inline function mocking
This commit is contained in:
@@ -518,6 +518,14 @@ from the defaults. We've tried to specify what the defaults are below.
|
||||
* `:include` will mock externed functions
|
||||
* `:exclude` will ignore externed functions (default).
|
||||
|
||||
* `:treat_inlines`:
|
||||
This specifies how you want CMock to handle functions that have been
|
||||
marked as inline in the header file. Should it mock them?
|
||||
|
||||
* `:include` will mock inlined functions
|
||||
* `:exclude` will ignore inlined 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
|
||||
@@ -686,4 +694,3 @@ 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. ;)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ class CMockConfig
|
||||
: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,
|
||||
|
||||
@@ -16,6 +16,7 @@ class CMockGenerator
|
||||
@prefix = @config.mock_prefix
|
||||
@suffix = @config.mock_suffix
|
||||
@weak = @config.weak
|
||||
@include_inline = @config.treat_inlines
|
||||
@ordered = @config.enforce_strict_ordering
|
||||
@framework = @config.framework.to_s
|
||||
@fail_on_unexpected_calls = @config.fail_on_unexpected_calls
|
||||
@@ -61,6 +62,12 @@ class CMockGenerator
|
||||
end
|
||||
|
||||
def create_mock_header_file(parsed_stuff)
|
||||
if @include_inline == :include
|
||||
@file_writer.create_file(@module_name + ".h", @subdir) do |file, filename|
|
||||
file << parsed_stuff[:normalized_source]
|
||||
end
|
||||
end
|
||||
|
||||
@file_writer.create_file(@mock_name + ".h", @subdir) do |file, filename|
|
||||
create_mock_header_header(file, filename)
|
||||
create_mock_header_service_call_declarations(file)
|
||||
|
||||
+73
-14
@@ -6,7 +6,7 @@
|
||||
|
||||
class CMockHeaderParser
|
||||
|
||||
attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs
|
||||
attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines
|
||||
|
||||
def initialize(cfg)
|
||||
@funcs = []
|
||||
@@ -24,13 +24,16 @@ class CMockHeaderParser
|
||||
@local_as_void = @treat_as_void
|
||||
@verbosity = cfg.verbosity
|
||||
@treat_externs = cfg.treat_externs
|
||||
@treat_inlines = cfg.treat_inlines
|
||||
@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
|
||||
|
||||
def parse(name, source)
|
||||
@module_name = name.gsub(/\W/,'')
|
||||
@typedefs = []
|
||||
@funcs = []
|
||||
@normalized_source = nil
|
||||
function_names = []
|
||||
|
||||
parse_functions( import_source(source) ).map do |decl|
|
||||
@@ -41,14 +44,62 @@ class CMockHeaderParser
|
||||
end
|
||||
end
|
||||
|
||||
@normalized_source = if (@treat_inlines == :include)
|
||||
transform_inline_functions(source)
|
||||
else
|
||||
''
|
||||
end
|
||||
|
||||
{ :includes => nil,
|
||||
:functions => @funcs,
|
||||
:typedefs => @typedefs
|
||||
:typedefs => @typedefs,
|
||||
:normalized_source => @normalized_source
|
||||
}
|
||||
end
|
||||
|
||||
private if $ThisIsOnlyATest.nil? ################
|
||||
|
||||
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
|
||||
|
||||
return source
|
||||
end
|
||||
|
||||
def transform_inline_functions(source)
|
||||
# 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.gsub!(/(static|inline)+.*\{.*\w*\}/m) do |m|
|
||||
m.gsub!(/(static|inline)/, '') # remove static and inline keywords
|
||||
m = remove_nested_pairs_of_braces(m)
|
||||
|
||||
# Functions having "{ }" at this point are/were inline functions,
|
||||
# Disguise them as normal functions with the ";"
|
||||
m.gsub!(/\s*\{\s\}/, ";")
|
||||
|
||||
# Cleanup the function declarations
|
||||
# Not strictly necessary, it will compile just fine, but it can help during debugging
|
||||
m_lines = m.split(/\s*;\s*/).uniq
|
||||
m_lines.each do |m_line|
|
||||
m_line.gsub!(/^\s+/, '') # remove extra white space from beginning of line
|
||||
m_line.gsub!(/\s+/, ' ') # remove remaining extra white space
|
||||
m_line.gsub!(/\n/, '') # remove newlines
|
||||
end
|
||||
|
||||
m_lines.join(";\n") + ";" # Join the lines and add the last semicolon manually
|
||||
end
|
||||
|
||||
return source
|
||||
end
|
||||
|
||||
def import_source(source)
|
||||
|
||||
# let's clean up the encoding in case they've done anything weird with the characters we might find
|
||||
@@ -100,16 +151,15 @@ class CMockHeaderParser
|
||||
"#{functype} #{$2.strip}(#{$3});"
|
||||
end
|
||||
|
||||
# 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
|
||||
source = remove_nested_pairs_of_braces(source)
|
||||
|
||||
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, ";")
|
||||
|
||||
@@ -124,11 +174,20 @@ class CMockHeaderParser
|
||||
src_lines = source.split(/\s*;\s*/).uniq
|
||||
src_lines.delete_if {|line| line.strip.length == 0} # remove blank lines
|
||||
src_lines.delete_if {|line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil?} #remove function pointer arrays
|
||||
if (@treat_externs == :include)
|
||||
src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?} # remove inline functions
|
||||
else
|
||||
src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern|inline)\s+/).nil?} # remove inline and extern functions
|
||||
|
||||
unless (@treat_externs == :include)
|
||||
src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern)\s+/).nil?} # remove extern functions
|
||||
end
|
||||
|
||||
if (@treat_inlines == :include)
|
||||
src_lines.each {
|
||||
|src_line|
|
||||
src_line.gsub!(/^inline/, "") # Remove "inline" so that they are 'normal' functions
|
||||
}
|
||||
else
|
||||
src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?} # remove inline functions
|
||||
end
|
||||
|
||||
src_lines.delete_if {|line| line.empty? } #drop empty lines
|
||||
end
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
:includes: []
|
||||
:mock_path: ./system/generated/
|
||||
:mock_prefix: mock_
|
||||
:treat_inlines: :include
|
||||
:treat_as_void:
|
||||
- OSEK_TASK
|
||||
- VOID_TYPE_CRAZINESS
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
static inline void dummy_func_0(void) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
inline static void dummy_func_1(int a) {
|
||||
int a = dummy_func_0();
|
||||
int b = 10;
|
||||
|
||||
return a + b;
|
||||
}
|
||||
|
||||
void inline static dummy_func_2(int a, char b, float c) {
|
||||
c += 3.14;
|
||||
b -= 32;
|
||||
return a + (int)(b) + c;
|
||||
}
|
||||
|
||||
void dummy_normal_func(int a);
|
||||
|
||||
inline void dummy_func_3(void) {
|
||||
//NOP
|
||||
}
|
||||
Executable → Regular
@@ -12,6 +12,7 @@
|
||||
:callback_after_arg_check: true
|
||||
:callback_include_count: false
|
||||
:treat_externs: :include
|
||||
:treat_inlines: :include
|
||||
|
||||
:systest:
|
||||
:types: |
|
||||
|
||||
@@ -19,6 +19,7 @@ describe CMockConfig, "Verify CMockConfig Module" do
|
||||
assert_equal(CMockConfig::CMockDefaultOptions[:attributes], config.attributes)
|
||||
assert_equal(CMockConfig::CMockDefaultOptions[:plugins], config.plugins)
|
||||
assert_equal(CMockConfig::CMockDefaultOptions[:treat_externs], config.treat_externs)
|
||||
assert_equal(CMockConfig::CMockDefaultOptions[:treat_inlines], config.treat_inlines)
|
||||
end
|
||||
|
||||
it "replace only options specified in a hash" do
|
||||
@@ -30,6 +31,7 @@ describe CMockConfig, "Verify CMockConfig Module" do
|
||||
assert_equal(test_attributes, config.attributes)
|
||||
assert_equal(CMockConfig::CMockDefaultOptions[:plugins], config.plugins)
|
||||
assert_equal(CMockConfig::CMockDefaultOptions[:treat_externs], config.treat_externs)
|
||||
assert_equal(CMockConfig::CMockDefaultOptions[:treat_inlines], config.treat_inlines)
|
||||
end
|
||||
|
||||
it "replace only options specified in a yaml file" do
|
||||
@@ -40,6 +42,7 @@ describe CMockConfig, "Verify CMockConfig Module" do
|
||||
assert_nil(config.includes)
|
||||
assert_equal(test_plugins, config.plugins)
|
||||
assert_equal(:include, config.treat_externs)
|
||||
assert_equal(:include, config.treat_inlines)
|
||||
end
|
||||
|
||||
it "populate treat_as map with internal standard_treat_as_map defaults, redefine defaults, and add custom values" do
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
:plugins:
|
||||
- 'soda'
|
||||
- 'pizza'
|
||||
:treat_externs: :include
|
||||
:treat_externs: :include
|
||||
:treat_inlines: :include
|
||||
|
||||
@@ -54,6 +54,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do
|
||||
@config.expect :includes_c_post_header, nil
|
||||
@config.expect :subdir, nil
|
||||
@config.expect :fail_on_unexpected_calls, true
|
||||
@config.expect :treat_inlines, :exclude
|
||||
@cmock_generator = CMockGenerator.new(@config, @file_writer, @utils, @plugins)
|
||||
@cmock_generator.module_name = @module_name
|
||||
@cmock_generator.mock_name = "Mock#{@module_name}"
|
||||
@@ -72,6 +73,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do
|
||||
@config.expect :includes_c_post_header, nil
|
||||
@config.expect :subdir, nil
|
||||
@config.expect :fail_on_unexpected_calls, true
|
||||
@config.expect :treat_inlines, :exclude
|
||||
@cmock_generator_strict = CMockGenerator.new(@config, @file_writer, @utils, @plugins)
|
||||
@cmock_generator_strict.module_name = @module_name
|
||||
@cmock_generator_strict.mock_name = "Mock#{@module_name}"
|
||||
@@ -133,6 +135,7 @@ describe CMockGenerator, "Verify CMockGenerator Module" do
|
||||
@config.expect :includes_c_post_header, nil
|
||||
@config.expect :subdir, nil
|
||||
@config.expect :fail_on_unexpected_calls, true
|
||||
@config.expect :treat_inlines, :exclude
|
||||
@cmock_generator2 = CMockGenerator.new(@config, @file_writer, @utils, @plugins)
|
||||
@cmock_generator2.module_name = "Pout-Pout Fish"
|
||||
@cmock_generator2.mock_name = "MockPout-Pout Fish"
|
||||
|
||||
@@ -23,6 +23,7 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
|
||||
@config.expect :when_no_prototypes, :error
|
||||
@config.expect :verbosity, 1
|
||||
@config.expect :treat_externs, :exclude
|
||||
@config.expect :treat_inlines, :exclude
|
||||
@config.expect :array_size_type, ['int', 'size_t']
|
||||
@config.expect :array_size_name, 'size|len'
|
||||
|
||||
@@ -453,6 +454,60 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
|
||||
assert_equal(expected, @parser.import_source(source).map!{|s|s.strip})
|
||||
end
|
||||
|
||||
it "leave inline functions if inline to be included" do
|
||||
source =
|
||||
"extern uint32 foobar(unsigned int);\n" +
|
||||
"uint32 extern_name_func(unsigned int);\n" +
|
||||
"uint32 funcinline(unsigned int);\n" +
|
||||
"inline void inlineBar(unsigned int);\n" +
|
||||
"extern int extern_bar(void);\n" +
|
||||
"static inline void staticinlineBar(unsigned int);\n" +
|
||||
"static inline void bar(unsigned int);\n" +
|
||||
"static inline void bar(unsigned int)\n" +
|
||||
"{\n" +
|
||||
" // NOP\n" +
|
||||
"}\n"
|
||||
|
||||
expected =
|
||||
[ "uint32 extern_name_func(unsigned int)",
|
||||
"uint32 funcinline(unsigned int)",
|
||||
"void inlineBar(unsigned int)",
|
||||
"void staticinlineBar(unsigned int)",
|
||||
"void bar(unsigned int)"
|
||||
]
|
||||
|
||||
@parser.treat_inlines = :include
|
||||
assert_equal(expected, @parser.import_source(source).map!{|s|s.strip})
|
||||
end
|
||||
|
||||
it "leave inline and extern functions if inline and extern to be included" do
|
||||
source =
|
||||
"extern uint32 foobar(unsigned int);\n" +
|
||||
"uint32 extern_name_func(unsigned int);\n" +
|
||||
"uint32 funcinline(unsigned int);\n" +
|
||||
"inline void inlineBar(unsigned int);\n" +
|
||||
"extern int extern_bar(void);\n" +
|
||||
"static inline void staticinlineBar(unsigned int);\n" +
|
||||
"static inline void bar(unsigned int);\n" +
|
||||
"static inline void bar(unsigned int)\n" +
|
||||
"{\n" +
|
||||
" // NOP\n" +
|
||||
"}\n"
|
||||
|
||||
expected =
|
||||
[ "extern uint32 foobar(unsigned int)",
|
||||
"uint32 extern_name_func(unsigned int)",
|
||||
"uint32 funcinline(unsigned int)",
|
||||
"void inlineBar(unsigned int)",
|
||||
"extern int extern_bar(void)",
|
||||
"void staticinlineBar(unsigned int)",
|
||||
"void bar(unsigned int)"
|
||||
]
|
||||
|
||||
@parser.treat_externs = :include
|
||||
@parser.treat_inlines = :include
|
||||
assert_equal(expected, @parser.import_source(source).map!{|s|s.strip})
|
||||
end
|
||||
|
||||
it "remove defines" do
|
||||
source =
|
||||
@@ -1725,4 +1780,115 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
|
||||
end
|
||||
end
|
||||
|
||||
it "Transform inline functions doesn't change a header with no inlines" do
|
||||
source =
|
||||
"#ifndef _NOINCLUDES\n" +
|
||||
"#define _NOINCLUDES\n" +
|
||||
"#include \"unity.h\"\n" +
|
||||
"#include \"cmock.h\"\n" +
|
||||
"#include \"YetAnotherHeader.h\"\n" +
|
||||
"\n" +
|
||||
"/* Ignore the following warnings since we are copying code */\n" +
|
||||
"#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" +
|
||||
"#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" +
|
||||
"#pragma GCC diagnostic push\n" +
|
||||
"#endif\n" +
|
||||
"#if !defined(__clang__)\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wpragmas\"\n" +
|
||||
"#endif\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" +
|
||||
"#endif\n" +
|
||||
"\n" +
|
||||
"struct my_struct {\n" +
|
||||
"int a;\n" +
|
||||
"int b;\n" +
|
||||
"int b;\n" +
|
||||
"char c;\n" +
|
||||
"};\n" +
|
||||
"int my_function(int a);\n" +
|
||||
"int my_better_function(struct my_struct *s);\n" +
|
||||
"\n" +
|
||||
"#endif _NOINCLUDES\n"
|
||||
|
||||
assert_equal(source, @parser.transform_inline_functions(source))
|
||||
end
|
||||
|
||||
it "Transform inline functions changes inline functions to function declarations" do
|
||||
source =
|
||||
"#ifndef _NOINCLUDES\n" +
|
||||
"#define _NOINCLUDES\n" +
|
||||
"#include \"unity.h\"\n" +
|
||||
"#include \"cmock.h\"\n" +
|
||||
"#include \"YetAnotherHeader.h\"\n" +
|
||||
"\n" +
|
||||
"/* Ignore the following warnings since we are copying code */\n" +
|
||||
"#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" +
|
||||
"#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" +
|
||||
"#pragma GCC diagnostic push\n" +
|
||||
"#endif\n" +
|
||||
"#if !defined(__clang__)\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wpragmas\"\n" +
|
||||
"#endif\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" +
|
||||
"#endif\n" +
|
||||
"\n" +
|
||||
"struct my_struct {\n" +
|
||||
"int a;\n" +
|
||||
"int b;\n" +
|
||||
"int b;\n" +
|
||||
"char c;\n" +
|
||||
"};\n" +
|
||||
"int my_function(int a);\n" +
|
||||
"int my_better_function(struct my_struct *s);\n" +
|
||||
"static inline int get_member_a(struct my_struct *s)\n" +
|
||||
"{\n" +
|
||||
" return s->a;\n" +
|
||||
"}\n" +
|
||||
"inline static int my_func_0(int a)\n" +
|
||||
"{\n" +
|
||||
" return a + 42;\n" +
|
||||
"}\n" +
|
||||
"inline int my_func_1(struct my_struct *s)\n" +
|
||||
"{\n" +
|
||||
" return get_member_a(s) + 42;\n" +
|
||||
"}\n" +
|
||||
"#endif _NOINCLUDES\n"
|
||||
|
||||
expected =
|
||||
"#ifndef _NOINCLUDES\n" +
|
||||
"#define _NOINCLUDES\n" +
|
||||
"#include \"unity.h\"\n" +
|
||||
"#include \"cmock.h\"\n" +
|
||||
"#include \"YetAnotherHeader.h\"\n" +
|
||||
"\n" +
|
||||
"/* Ignore the following warnings since we are copying code */\n" +
|
||||
"#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" +
|
||||
"#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" +
|
||||
"#pragma GCC diagnostic push\n" +
|
||||
"#endif\n" +
|
||||
"#if !defined(__clang__)\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wpragmas\"\n" +
|
||||
"#endif\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" +
|
||||
"#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" +
|
||||
"#endif\n" +
|
||||
"\n" +
|
||||
"struct my_struct {\n" +
|
||||
"int a;\n" +
|
||||
"int b;\n" +
|
||||
"int b;\n" +
|
||||
"char c;\n" +
|
||||
"};\n" +
|
||||
"int my_function(int a);\n" +
|
||||
"int my_better_function(struct my_struct *s);\n" +
|
||||
"int get_member_a(struct my_struct *s);\n" +
|
||||
"int my_func_0(int a);\n" +
|
||||
"int my_func_1(struct my_struct *s);\n" +
|
||||
"#endif _NOINCLUDES\n"
|
||||
|
||||
assert_equal(expected, @parser.transform_inline_functions(source))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user