33 Commits

Author SHA1 Message Date
mvandervoord 543c230f33 bump version 2019-11-12 18:27:33 -05:00
Mark VanderVoord 968d3f6ec4 Merge pull request #263 from laurensmiers/master
Inline function mocking refactor
2019-11-12 18:26:32 -05:00
laurens 0c45d26a28 Rename total_levels to total_pairs
- We are counting pairs of braces, not levels of indentation
2019-11-13 00:03:52 +01:00
laurens 811b85e1b1 Use <counter>.times iso until <condition> 2019-11-13 00:03:30 +01:00
laurens 759d1826c8 Add test with no braces in source
- Should return 0 since no braces in source
2019-11-12 20:28:08 +01:00
laurens 60a1829acf Fix comments 2019-11-12 20:04:30 +01:00
laurens 8ba3ed99a1 Extract count_number_of_pairs_of_braces_in_function method 2019-11-12 20:01:09 +01:00
laurens 0af0e20d15 Refactor transform_inline_functions
- Just looking for static|inline in the gsub is a bit too aggressive.
  Instead, look for the "static inline" and parse it:
  - 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.
  - Copy everything after the inline function implementation

Repeat the above step until we can't find "static inline" anymore

- To remove the inline implementation,
  we count the number of square-bracket 'levels' in the inline function
  and remove every pair. This ensures that constructs like:
  inline void func(void) {
      if (...) {
        //NOP
      }
      else
      {
        //NOP
      }
  }
  will not end up leaving the 'else' keyword unremoved:
  inline void func(void);
      else
  If we count the number of levels, we don't end up in this scenario
  since we remove 3 'pairs', the if-one, the else-one and finally the
  main body.
2019-11-12 20:01:04 +01:00
laurens 2def6c4f21 Fix compile warnings on inline.h test header 2019-11-12 12:10:27 +01:00
Mark VanderVoord d403eb88c8 Merge pull request #258 from jlindgren90/master
Fix compile error when :unity_helper_path is relative.
2019-11-11 08:38:14 -05:00
Mark VanderVoord 18b2deca85 Merge pull request #261 from laurensmiers/master
Inline function mocking
2019-11-11 08:36:21 -05:00
laurens bd91eb4cca Refactor calling of transform_inline_functions
- This way, @normalized_source will always be defined, which it should
  because we are referencing it a few lines down, thanks to
  @mvandervoord for the suggestion
2019-11-11 14:26:29 +01:00
laurens fe829f2d8a Expand transform_inline_functions unit test with more usecases 2019-11-11 13:26:40 +01:00
laurens 5650b79dcd Replace NOP with actual operations to test inline.h compilation 2019-11-11 13:22:00 +01:00
laurens 9908930bc5 Fix multi-line each + use gsub! iso gsub 2019-11-11 13:14:18 +01:00
laurens b256013c5e Only normalize source if treat_inlines is enabled 2019-11-11 13:12:15 +01:00
laurens 927ca1bec0 Extract method to remove_nested_pair_of_braces in cmock_header_parser 2019-11-10 20:55:02 +01:00
laurens e1c6851492 Rename normalize_source to transform_inline_functions 2019-11-10 20:13:35 +01:00
laurens e0a61d484e Add unit tests for normalize_source
- Refactor normalize_source to do the minimum amount that is
  necessary.
  For now, this is just removing inline functions.
2019-11-10 20:11:27 +01:00
laurens 431c5c678a Refactor cleanup actions when normalizing source 2019-11-10 19:35:27 +01:00
laurens 3a07201a3f Add treat_inlines to all_plugins_coexist test 2019-11-10 18:44:20 +01:00
laurens 11dfb9c668 osek.h does not need to be executable 2019-11-10 18:39:35 +01:00
laurens 27e89dd05e Rename treat_inline to treat_inlines 2019-11-10 18:39:35 +01:00
laurens 237c02dade Write converted header file (without inline) to separate file
- This prevents changing the original mock file that is generated,
  it thinks it includes the 'original' header file that is being
  mocked,
  but if we place our newly generated header file in the same
  directory as the mock_<header_file>.h, it will include our newly
  generated header file.
2019-11-10 18:39:20 +01:00
laurens 5ee470d7e4 Convert inline functions in header-mock to 'normal' functions
- normalize_source() will convert the header-to-mock.
  It will look for the inline function definitions and transform them
  into function declarations for non-inline functions.
2019-11-10 18:39:20 +01:00
laurensmiers 79ee5b8419 Add documentation 2019-11-10 18:39:20 +01:00
laurensmiers fba3bc22a2 Some more tests for :treat_include: with extern + some comments 2019-11-10 18:39:20 +01:00
laurensmiers 01536c4a67 Better handling of inline functions with declaration in header 2019-11-10 18:39:20 +01:00
laurensmiers 7a32429bd2 Add handling of treat_inline
- treat_externs only has effect on "extern", not "inline" anymore
- when treat_inline = :include, we remove the "inline" so that the inline functions become 'normal' functions
  - they are handled as as usual and will be mocked
- Add tests to check inline functions are not deleted when treat_inline=:include (and thus mocked)
2019-11-10 18:39:20 +01:00
laurensmiers dbb2f4964c Add treat_inline configuration option 2019-11-10 18:39:20 +01:00
John Lindgren 2817da62db Fix compile error when :unity_helper_path is relative.
Including :unity_helper_path directly doesn't work if the path is
relative and :mock_path is not the same as the working directory.
2019-11-05 15:39:32 -05:00
Mark VanderVoord ee3ae84e64 Merge pull request #257 from art-of-dom/ret-var-name-change
make ret variable more unique to prevent collision with with code under test
2019-11-04 23:21:09 -05:00
Dom Postorivo 0cdc742538 make ret variable more unique to prevent collision with with code under test 2019-11-04 11:23:57 -05:00
15 changed files with 462 additions and 24 deletions
+8 -1
View File
@@ -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. ;)
+11 -1
View File
@@ -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,
@@ -64,7 +65,16 @@ class CMockConfig
end
options[:unity_helper_path] ||= options[:unity_helper]
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].map! {|p| p.to_sym}
@options = options
+7
View File
@@ -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)
+2 -2
View File
@@ -60,9 +60,9 @@ class CMockGeneratorPluginCallback
" UNITY_CLR_DETAILS();\n" \
" return;\n }\n"
else
" #{function[:return][:type]} ret = #{generate_call(function)};\n" \
" #{function[:return][:type]} cmock_cb_ret = #{generate_call(function)};\n" \
" UNITY_CLR_DETAILS();\n" \
" return ret;\n }\n"
" return cmock_cb_ret;\n }\n"
end
end
+124 -14
View File
@@ -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,113 @@ 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
# 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 0 != curr_level
total_pairs = 0 # Something is fishy about this source, not enough closing braces?
end
return 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)
# Format to look for inline functions.
# This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static")
# There are several possibilities:
# - sometimes they appear together, sometimes individually,
# - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff),
# so we check for word boundaries when searching for them
# - We first remove "static inline" combinations and boil down to single inline or static statements
inline_function_regex_formats = [
/(static\s+inline|inline\s+static)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output)
/(\bstatic\b|\binline\b)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output)
]
square_bracket_pair_regex_format = /\{[^\{\}]*\}/ # Regex to match one whole block enclosed by two square brackets
# 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)
# - 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 "static inline" and parse it:
# - 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
inline_function_regex_formats.each do |format|
loop do
inline_function_match = source.match(/#{format}/) # Search for inline function declaration
break if nil == inline_function_match # No inline functions so nothing to do
total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match)
break if 0 == total_pairs_to_remove # 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
source = inline_function_match.pre_match + inline_function_stripped # Make new source with the inline function removed and move on to the next
end
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 +202,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 +225,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
+1 -1
View File
@@ -11,7 +11,7 @@
#define CMOCK_VERSION_MAJOR 2
#define CMOCK_VERSION_MINOR 5
#define CMOCK_VERSION_BUILD 0
#define CMOCK_VERSION_BUILD 1
#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
+1
View File
@@ -4,6 +4,7 @@
:includes: []
:mock_path: ./system/generated/
:mock_prefix: mock_
:treat_inlines: :include
:treat_as_void:
- OSEK_TASK
- VOID_TYPE_CRAZINESS
+23
View File
@@ -0,0 +1,23 @@
static inline int dummy_func_0(void) {
return 5;
}
inline static int dummy_func_1(int a) {
int a = dummy_func_0();
int b = 10;
return a + b;
}
int inline static dummy_func_2(int a, char b, float c) {
c += 3.14;
b -= 32;
return a + (int)(b) + (int)c;
}
void dummy_normal_func(int a);
inline void dummy_func_3(void) {
//NOP
}
View File
@@ -12,6 +12,7 @@
:callback_after_arg_check: true
:callback_include_count: false
:treat_externs: :include
:treat_inlines: :include
:systest:
:types: |
+3
View File
@@ -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 -1
View File
@@ -2,4 +2,5 @@
:plugins:
- 'soda'
- 'pizza'
:treat_externs: :include
:treat_externs: :include
:treat_inlines: :include
+3
View File
@@ -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"
@@ -191,9 +191,9 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
expected = [" if (!Mock.Apple_CallbackBool &&\n",
" Mock.Apple_CallbackFunctionPointer != NULL)\n",
" {\n",
" int ret = Mock.Apple_CallbackFunctionPointer(Mock.Apple_CallbackCalls++);\n",
" int cmock_cb_ret = Mock.Apple_CallbackFunctionPointer(Mock.Apple_CallbackCalls++);\n",
" UNITY_CLR_DETAILS();\n",
" return ret;\n",
" return cmock_cb_ret;\n",
" }\n"
].join
returned = @cmock_generator_plugin_callback.mock_implementation_precheck(function)
@@ -246,9 +246,9 @@ describe CMockGeneratorPluginCallback, "Verify CMockGeneratorPluginCallback Modu
expected = [" if (!Mock.Apple_CallbackBool &&\n",
" Mock.Apple_CallbackFunctionPointer != NULL)\n",
" {\n",
" int ret = Mock.Apple_CallbackFunctionPointer(steak, flag, Mock.Apple_CallbackCalls++);\n",
" int cmock_cb_ret = Mock.Apple_CallbackFunctionPointer(steak, flag, Mock.Apple_CallbackCalls++);\n",
" UNITY_CLR_DETAILS();\n",
" return ret;\n",
" return cmock_cb_ret;\n",
" }\n"
].join
returned = @cmock_generator_plugin_callback.mock_implementation_precheck(function)
+272
View File
@@ -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,221 @@ 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 staticinlinebar(struct my_struct *s)\n" + # This is a function with a lot of indentation levels, we should be able to handle it
"{\n" +
"\t{\n" +
"\t\t{\n" +
"\t\t\treturn s->a;\n" +
"\t\t}\n" +
"\t}\n" +
"}\n" +
"static inline int staticinlinefunc(struct my_struct *s)\n" +
"{\n" +
" return s->a;\n" +
"}\n" +
"int bar(struct my_struct *s);\n" + # A normal function to screw with our parser
"inline static int inlinestaticfunc(int a) {\n" +
" return a + 42;\n" +
"}\n" +
"inline int StaticInlineFunc(struct my_struct *s)\n" +
"{\n" +
" return get_member_a(s) + 42;\n" +
"}\n" +
"int inline StaticInlineBar(struct my_struct *s)\n" +
"{\n" +
" return get_member_a(s) + 42;\n" +
"}\n" +
"struct staticinlinestruct {\n" + # Add a structure declaration between the inline functions, just to make sure we don't touch it!
"int a;\n" +
"};\n" +
"\n" +
"struct staticinlinestruct fubarstruct(struct my_struct *s);\n" + # Another normal function to screw with our parser
"static inline struct staticinlinestruct inlinefubarfunction(struct my_struct *s)\n" +
"{\n" +
" return (struct staticinlinestruct)*s;\n" +
"}\n" +
"int fubar(struct my_struct *s);\n" + # Another normal function to screw with our parser
"inline int stuff(int num)" +
"{" +
" int reg = 0x12;" +
" if (num > 0)" +
" {" +
" reg |= (0x0Eu);" +
" }" +
" else" +
" {" +
" reg |= (0x07u);" +
" }" +
" return reg;" +
"}" +
"\n" +
"int inline static dummy_func_2(int a, char b, float c) {" + # This is a sneaky one, inline static is placed AFTER the return value
" c += 3.14;" +
" b -= 32;" +
" return a + (int)(b) + (int)c;" +
"}" +
"#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 staticinlinebar(struct my_struct *s);\n" +
"int staticinlinefunc(struct my_struct *s);\n" +
"int bar(struct my_struct *s);\n" +
"int inlinestaticfunc(int a);\n" +
"int StaticInlineFunc(struct my_struct *s);\n" +
"int StaticInlineBar(struct my_struct *s);\n" +
"struct staticinlinestruct {\n" +
"int a;\n" +
"};\n" +
"\n" +
"struct staticinlinestruct fubarstruct(struct my_struct *s);\n" +
"struct staticinlinestruct inlinefubarfunction(struct my_struct *s);\n" +
"int fubar(struct my_struct *s);\n" +
"int stuff(int num);\n" +
"int dummy_func_2(int a, char b, float c);" +
"#endif _NOINCLUDES\n"
assert_equal(expected, @parser.transform_inline_functions(source))
end
it "Count number of pairs of braces in function succesfully" do
source =
"int foo(struct my_struct *s)\n" +
"{\n" +
" return get_member_a(s) + 42;\n" +
"}\n"
complex_source =
"int bar(struct my_struct *s)\n" +
"{\n" +
"\tint a = 6;\n" +
"\tint res = foo(&(struct my_struct){.nr = a});\n" +
"\t{\n" +
"\t\tint a = 5;\n" +
"\t\tint res = foo(&(struct my_struct){.nr = a});\n" +
"\t\t{\n" +
"\t\t\tstruct my_struct a = {.nr = 1};\n" +
"\t\t}\n" +
"\t}\n" +
"\treturn 42;\n" +
"}\n"
assert_equal(1, @parser.count_number_of_pairs_of_braces_in_function(source))
assert_equal(6, @parser.count_number_of_pairs_of_braces_in_function(complex_source))
end
it "Count number of pairs of braces returns 0 if bad source is supplied" do
bad_source_0 =
"int foo(struct my_struct *s)\n" +
"{\n" +
" return get_member_a(s) + 42;\n" +
"\n" # Missing closing brace
bad_source_1 =
"int bar(struct my_struct *s)\n" +
"{\n" +
"\tint a = 6;\n" +
"\tint res = foo(&(struct my_struct){.nr = a});\n" +
"\t{\n" +
"\t\tint a = 5;\n" +
"\t\tint res = foo(&(struct my_struct){.nr = a});\n" +
"\t\t{\n" +
"\t\t\n" + # Missing closing brace
"\t}\n" +
"\treturn 42;\n" +
"}\n"
bad_source_2 =
"int foo(struct my_struct *s)\n" +
"\n" # No braces in source
assert_equal(0, @parser.count_number_of_pairs_of_braces_in_function(bad_source_0))
assert_equal(0, @parser.count_number_of_pairs_of_braces_in_function(bad_source_1))
assert_equal(0, @parser.count_number_of_pairs_of_braces_in_function(bad_source_2))
end
end