Files
CMock/test/unit/cmock_header_parser_test.rb
T
alufers 3caf511b8f fix: Don't smush macros which have an escaped empty line at the end
nanopb has a habit of generating macros which end with an escaped empty line (for example for messages which are empty). They look like this:

   #define some_msg_t_FIELDLIST(X, a) \

   #define some_msg_t_CALLBACK NULL

This caused CMock to strip all of the newlines after the backslash instead of only one, which the backslash was escaping.
This in turn caused both the macros to be in one line, causing a compile error in the generated mock.
2023-04-26 14:16:41 +02:00

2886 lines
113 KiB
Ruby

# ==========================================
# CMock Project - Automatic Mock Generation for C
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
# [Released under MIT License. Please refer to license.txt for details]
# ==========================================
$ThisIsOnlyATest = true
require File.expand_path(File.dirname(__FILE__)) + "/../test_helper"
require File.expand_path(File.dirname(__FILE__)) + '/../../lib/cmock_header_parser'
describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
before do
create_mocks :config
@config.expect :strippables, ["STRIPPABLE"]
@config.expect :attributes, ['__ramfunc', 'funky_attrib', 'SQLITE_API']
@config.expect :c_calling_conventions, ['__stdcall']
@config.expect :treat_as_void, ['MY_FUNKY_VOID']
@config.expect :treat_as, { "BANJOS" => "INT", "TUBAS" => "HEX16"}
@config.expect :treat_as_array, {"IntArray" => "int", "Book" => "Page"}
@config.expect :when_no_prototypes, :error
@config.expect :verbosity, 1
@config.expect :treat_externs, :exclude
@config.expect :treat_inlines, :exclude
@config.expect :inline_function_patterns, ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*']
@config.expect :array_size_type, ['int', 'size_t']
@config.expect :array_size_name, 'size|len'
@parser = CMockHeaderParser.new(@config)
@test_project = {
:module_name => 'test_file.h',
:typedefs => [],
:functions => [],
:normalized_source => nil
}
end
after do
end
it "create and initialize variables to defaults appropriately" do
assert_nil(@parser.funcs)
assert_equal(['const', '__ramfunc', 'funky_attrib', 'SQLITE_API'], @parser.c_attributes)
assert_equal(['void','MY_FUNKY_VOID'], @parser.treat_as_void)
end
it "strip out line comments" do
source =
" abcd;\n" +
"// hello;\n" +
"who // is you\n"
expected =
[
"abcd",
"who"
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove block comments" do
source =
" no_comments;\n" +
"// basic_line_comment;\n" +
"/* basic_block_comment;*/\n" +
"pre_block; /* start_of_block_comment;\n" +
"// embedded_line_comment_in_block_comment; */\n" +
"// /* commented_out_block_comment_line\n" +
"shown_because_block_comment_invalid_from_line_comment;\n" +
"// */\n" +
"//* shorter_commented_out_block_comment_line; \n" +
"shown_because_block_comment_invalid_from_shorter_line_comment;\n" +
"/*/\n" +
"not_shown_because_line_above_started_comment;\n" +
"//*/\n" +
"/* \n" +
"not_shown_because_block_comment_started_this_time;\n" +
"/*/\n" +
"shown_because_line_above_ended_comment_this_time;\n" +
"//*/\n"
expected =
[
"no_comments",
"pre_block",
"shown_because_block_comment_invalid_from_line_comment",
"shown_because_block_comment_invalid_from_shorter_line_comment",
"shown_because_line_above_ended_comment_this_time"
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove strippables from the beginning or end of function declarations" do
source =
"void* my_calloc(size_t, size_t) STRIPPABLE;\n" +
"void\n" +
" my_realloc(void*, size_t) STRIPPABLE;\n" +
"extern int\n" +
" my_printf (void *my_object, const char *my_format, ...)\n" +
" STRIPPABLE;\n" +
" void STRIPPABLE universal_handler ();\n"
expected =
[
"void* my_calloc(size_t, size_t)",
"void my_realloc(void*, size_t)",
"void universal_handler()"
]
assert_equal(expected, @parser.import_source(source, @test_project))
end
it "remove gcc's function __attribute__'s" do
source =
"void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)));\n" +
"void\n" +
" my_realloc(void*, size_t) __attribute__((alloc_size(2)));\n" +
"extern int\n" +
" my_printf (void *my_object, const char *my_format, ...)\n" +
" __attribute__ ( (format (printf, 2, 3)) );\n" +
" void __attribute__ ((interrupt)) universal_handler ();\n"
expected =
[
"void* my_calloc(size_t, size_t)",
"void my_realloc(void*, size_t)",
"void universal_handler()"
]
assert_equal(expected, @parser.import_source(source, @test_project))
end
it "remove preprocessor directives" do
source =
"#when stuff_happens\n" +
"#ifdef _TEST\n" +
"#pragma stack_switch"
expected = []
assert_equal(expected, @parser.import_source(source, @test_project))
end
it "remove assembler pragma sections" do
source =
" #pragma\tasm\n" +
" .foo\n" +
" lda %m\n" +
" nop\n" +
"# pragma endasm \n" +
"foo"
expected = ["foo"]
assert_equal(expected, @parser.import_source(source, @test_project))
end
it "smush lines together that contain continuation characters" do
source =
"hoo hah \\\n" +
"when \\ \n"
expected =
[
"hoo hah when"
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove C macro definitions" do
source =
"#define this is the first line\\\n" +
"and the second\\\n" +
"and the third that should be removed\n" +
"but I'm here\n"
expected = ["but I'm here"]
assert_equal(expected, @parser.import_source(source, @test_project))
end
it "remove typedef statements" do
source =
"typedef uint32 (unsigned int);\n" +
"const typedef int INT;\n" +
"int notatypedef;\n" +
"int typedef_isnt_me;\n" +
" typedef who cares what really comes here \n" + # exercise multiline typedef
" continuation;\n" +
"this should remain!;\n" +
"typedef blah bleh;\n" +
"typedef struct shell_command_struct {\n" +
" char_ptr COMMAND;\n" +
" int_32 (*SHELL_FUNC)(int_32 argc);\n" +
"} SHELL_COMMAND_STRUCT, * SHELL_COMMAND_PTR;\n" +
"typedef struct shell_command_struct {\n" +
" char_ptr COMMAND;\n" +
" int_32 (*SHELL_FUNC)(int_32 argc, char_ptr argv[]);\n" +
"} SHELL_COMMAND_STRUCT, * SHELL_COMMAND_PTR;\n" +
"typedef struct shell_command_struct {\n" +
" char_ptr COMMAND;\n" +
" int_32 (*SHELL_FUNC)(int_32 argc);\n" +
"};\n"
expected =
[
"int notatypedef",
"int typedef_isnt_me",
"this should remain!"
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove enum statements" do
source =
"enum _NamedEnum {\n" +
" THING1 = (0x0001),\n" +
" THING2 = (0x0001 << 5),\n" +
"}ListOValues;\n\n" +
"don't delete me!!\n" +
" modifier_str enum _NamedEnum {THING1 = (0x0001), THING2 = (0x0001 << 5)} ListOValues;\n\n" +
"typedef enum {\n" +
" THING1,\n" +
" THING2,\n" +
"} Thinger;\n" +
"or me!!\n"
assert_equal(["don't delete me!! or me!!"], @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove union statements" do
source =
"union _NamedDoohicky {\n" +
" unsigned int a;\n" +
" char b;\n" +
"} Doohicky;\n\n" +
"I want to live!!\n" +
"some_modifier union { unsigned int a; char b;} Whatever;\n" +
"typedef union {\n" +
" unsigned int a;\n" +
" char b;\n" +
"} Whatever;\n" +
"me too!!\n"
assert_equal(["I want to live!! me too!!"], @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove struct statements" do
source =
"struct _NamedStruct1 {\n" +
" unsigned int a;\n" +
" signed long int b;\n" +
"} Thing ;\n\n" +
"extern struct ForwardDeclared_t TestDataType1;\n" +
"void foo(void);\n" +
"struct\n"+
" MultilineForwardDeclared_t\n" +
" TestDataType2;\n" +
"struct THINGER foo(void);\n" +
"typedef struct {\n" +
" unsigned int a;\n" +
" signed char b;\n" +
"}Thinger;\n" +
"I want to live!!\n"
assert_equal(["void foo(void)", "struct THINGER foo(void)", "I want to live!!"],
@parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove externed and inline functions" do
source =
" extern uint32 foobar(unsigned int);\n" +
"uint32 extern_name_func(unsigned int);\n" +
"uint32 funcinline(unsigned int);\n" +
"extern void bar(unsigned int);\n" +
"inline void bar(unsigned int);\n" +
"extern\n" +
"void kinda_ugly_on_the_next_line(unsigned int);\n"
expected =
[
"uint32 extern_name_func(unsigned int)",
"uint32 funcinline(unsigned int)"
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove function definitions but keep function declarations" do
source =
"uint32 func_with_decl_a(unsigned int);\n" +
"uint32 func_with_decl_a(unsigned int a) { return a; }\n" +
"uint32 func_with_decl_b(unsigned int);\n" +
"uint32 func_with_decl_b(unsigned int a)\n" +
"{\n" +
" bar((unsigned int) a);\n" +
" stripme(a);\n" +
"}\n"
expected =
[
"uint32 func_with_decl_a(unsigned int)",
"uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function
"uint32 func_with_decl_b(unsigned int)",
"uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove function definitions with nested braces but keep function declarations" do
source =
"uint32 func_with_decl_a(unsigned int);\n" +
"uint32 func_with_decl_a(unsigned int a) {\n" +
" while (stuff) {\n" +
" not_a_definition1(void);\n" +
" }\n" +
" not_a_definition2(blah, bleh);\n" +
" return a;\n" +
"}\n" +
"uint32 func_with_decl_b(unsigned int);\n" +
"uint32 func_with_decl_b(unsigned int a)\n" +
"{\n" +
" bar((unsigned int) a);\n" +
" stripme(a);\n" +
"}\n" +
"uint32 func_with_decl_c(unsigned int);\n" +
"uint32 func_with_decl_c(unsigned int a)\n" +
"{\n" +
" if(a > 0)\n" +
" {\n" +
" return 1;\n" +
" }\n" +
" else\n"+
" {\n" +
" return 2;\n" +
" }\n" +
"}\n"
expected =
[
"uint32 func_with_decl_a(unsigned int)",
"uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function
"uint32 func_with_decl_b(unsigned int)",
"uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function
"uint32 func_with_decl_c(unsigned int)",
"uint32 func_with_decl_c", #okay. it's not going to be interpretted as another function
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove a fully defined inline function" do
source =
"inline void foo(unsigned int a) { oranges = a; }\n" +
"inline void bar(unsigned int a) { apples = a; };\n" +
"inline void bar(unsigned int a)\n" +
"{" +
" bananas = a;\n" +
"}"
# ensure it's expected type of exception
assert_raises RuntimeError do
@parser.parse("module", source)
end
assert_nil(@parser.funcs)
# verify exception message
begin
@parser.parse("module", source)
rescue RuntimeError => e
assert_equal("ERROR: No function prototypes found!", e.message)
end
end
it "remove a fully defined inline function that is multiple lines" do
source =
"inline void bar(unsigned int a)\n" +
"{" +
" bananas = a;\n" +
" grapes = a;\n" +
" apples(bananas, grapes);\n" +
"}"
# ensure it's expected type of exception
assert_raises RuntimeError do
@parser.parse("module", source)
end
assert_nil(@parser.funcs)
# verify exception message
begin
@parser.parse("module", source)
rescue RuntimeError => e
assert_equal("ERROR: No function prototypes found!", e.message)
end
end
it "remove a fully defined inline function that contains nested braces" do
source =
"inline void bar(unsigned int a)\n" +
"{" +
" apples(bananas, grapes);\n" +
" if (bananas == a)\n" +
" {\n" +
" oranges(a);\n" +
" grapes = a;\n" +
" }\n" +
" grapefruit(bananas, grapes);\n" +
"}"
# ensure it's expected type of exception
assert_raises RuntimeError do
@parser.parse("module", source)
end
assert_nil(@parser.funcs)
# verify exception message
begin
@parser.parse("module", source)
rescue RuntimeError => e
assert_equal("ERROR: No function prototypes found!", e.message)
end
end
it "remove just inline functions if externs to be included" do
source =
" extern uint32 foobar(unsigned int);\n" +
"uint32 extern_name_func(unsigned int);\n" +
"uint32 funcinline(unsigned int);\n" +
"extern void bar(unsigned int);\n" +
"inline void bar(unsigned int);\n" +
"extern\n" +
"void kinda_ugly_on_the_next_line(unsigned int);\n"
expected =
[ "extern uint32 foobar(unsigned int)",
"uint32 extern_name_func(unsigned int)",
"uint32 funcinline(unsigned int)",
"extern void bar(unsigned int)",
"extern void kinda_ugly_on_the_next_line(unsigned int)"
]
@parser.treat_externs = :include
assert_equal(expected, @parser.import_source(source, @test_project).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, @test_project).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, @test_project).map!{|s|s.strip})
end
it "Include inline functions that contain user defined inline function formats" do
source =
"uint32 foo(unsigned int);\n" +
"uint32 bar(unsigned int);\n" +
"inline void inlineBar(void)\n" +
"{\n" +
" return 43;\n" +
"}\n" +
"static __inline__ __attribute__ ((always_inline)) int alwaysinlinefunc(int a)\n" +
"{\n" +
" return a + inlineBar();\n" +
"}\n" +
"static __inline__ void inlinebar(unsigned int)\n" +
"{\n" +
" int a = alwaysinlinefunc()\n" +
"}\n"
expected =
[
"uint32 foo(unsigned int)",
"uint32 bar(unsigned int)",
"void inlineBar(void)",
"int alwaysinlinefunc(int a)",
"void inlinebar(unsigned int)"
]
@parser.treat_inlines = :include
@parser.inline_function_patterns = ['static __inline__ __attribute__ \(\(always_inline\)\)', 'static __inline__', '\binline\b']
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove defines" do
source =
"#define whatever you feel like defining\n" +
"void hello(void);\n" +
"#DEFINE I JUST DON'T CARE\n" +
"#deFINE\n" +
"#define get_foo() \\\n ((Thing)foo.bar)" # exercise multiline define
expected =
[
"void hello(void)",
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "remove keywords that would keep things from going smoothly in the future" do
source =
"const int TheMatrix(register int Trinity, unsigned int *restrict Neo)"
expected =
[
"const int TheMatrix(int Trinity, unsigned int * Neo)",
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
# some code actually typedef's void even though it's not ANSI C and is, frankly, weird
# since cmock treats void specially, we can't let void be obfuscated
it "handle odd case of typedef'd void returned" do
source = "MY_FUNKY_VOID FunkyVoidReturned(int a)"
expected = { :var_arg=>nil,
:name=>"FunkyVoidReturned",
:unscoped_name=>"FunkyVoidReturned",
:namespace=>[],
:class=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:modifier=>"",
:contains_ptr? => false,
:args=>[{:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false}],
:args_string=>"int a",
:args_call=>"a"}
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "handle odd case of typedef'd void as arg" do
source = "int FunkyVoidAsArg(MY_FUNKY_VOID)"
expected = { :var_arg=>nil,
:name=>"FunkyVoidAsArg",
:unscoped_name=>"FunkyVoidAsArg",
:namespace=>[],
:class=>nil,
:return=>{ :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:modifier=>"",
:contains_ptr? => false,
:args=>[],
:args_string=>"void",
:args_call=>"" }
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "handle odd case of typedef'd void as arg pointer" do
source = "char FunkyVoidPointer(MY_FUNKY_VOID* bluh)"
expected = { :var_arg=>nil,
:name=>"FunkyVoidPointer",
:unscoped_name=>"FunkyVoidPointer",
:namespace=>[],
:class=>nil,
:return=>{ :type => "char",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "char cmock_to_return",
:void? => false
},
:modifier=>"",
:contains_ptr? => true,
:args=>[{:type=>"MY_FUNKY_VOID*", :name=>"bluh", :ptr? => true, :const? => false, :const_ptr? => false}],
:args_string=>"MY_FUNKY_VOID* bluh",
:args_call=>"bluh" }
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "strip default values from function parameter lists" do
source =
"void Foo(int a = 57, float b=37.52, char c= 'd', char* e=\"junk\");\n"
expected =
[
"void Foo(int a, float b, char c, char* e)"
]
assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip})
end
it "raise upon empty file" do
source = ''
# ensure it's expected type of exception
assert_raises RuntimeError do
@parser.parse("module", source)
end
assert_nil(@parser.funcs)
# verify exception message
begin
@parser.parse("module", source)
rescue RuntimeError => e
assert_equal("ERROR: No function prototypes found!", e.message)
end
end
it "clean up module names that contain spaces, dashes, and such" do
source = 'void meh(int (*func)(int));'
retval = @parser.parse("C:\Ugly Module-Name", source)
assert (retval[:typedefs][0] =~ /CUglyModuleName/)
end
it "raise upon no function prototypes found in file" do
source =
"typedef void SILLY_VOID_TYPE1;\n" +
"typedef (void) SILLY_VOID_TYPE2 ;\n" +
"typedef ( void ) (*FUNCPTR)(void);\n\n" +
"#define get_foo() \\\n ((Thing)foo.bar)"
# ensure it's expected type of exception
assert_raises(RuntimeError) do
@parser.parse("module", source)
end
assert_nil(@parser.funcs)
# verify exception message
begin
@parser.parse("module", source)
rescue RuntimeError => e
assert_equal("ERROR: No function prototypes found!", e.message)
end
end
it "raise upon prototype parsing failure" do
source = "void (int, )"
# ensure it's expected type of exception
assert_raises(RuntimeError) do
@parser.parse("module", source)
end
# verify exception message
begin
@parser.parse("module", source)
rescue RuntimeError => e
assert(e.message.include?("Failed Parsing Declaration Prototype!"))
end
end
it "extract and return function declarations with retval and args" do
source = "int Foo(int a, unsigned int b)"
expected = { :var_arg=>nil,
:name=>"Foo",
:unscoped_name=>"Foo",
:namespace=>[],
:class=>nil,
:return=>{ :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned int", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"int a, unsigned int b",
:args_call=>"a, b" }
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "extract and return function declarations with no retval" do
source = "void FunkyChicken( uint la, int de, bool da)"
expected = { :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyChicken",
:unscoped_name=>"FunkyChicken",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"uint", :name=>"la", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int", :name=>"de", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"bool", :name=>"da", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"uint la, int de, bool da",
:args_call=>"la, de, da" }
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "extract and return function declarations with implied voids" do
source = "void tat()"
expected = { :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"tat",
:unscoped_name=>"tat",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ ],
:args_string=>"void",
:args_call=>"" }
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "extract modifiers properly" do
source = "const int TheMatrix(int Trinity, unsigned int * Neo)"
expected = { :var_arg=>nil,
:return=>{ :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => true,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"TheMatrix",
:unscoped_name=>"TheMatrix",
:namespace=>[],
:class=>nil,
:modifier=>"const",
:contains_ptr? => true,
:args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false}
],
:args_string=>"int Trinity, unsigned int* Neo",
:args_call=>"Trinity, Neo" }
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "extract c calling conventions properly" do
source = "const int __stdcall TheMatrix(int Trinity, unsigned int * Neo)"
expected = { :var_arg=>nil,
:return=>{ :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => true,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"TheMatrix",
:unscoped_name=>"TheMatrix",
:namespace=>[],
:class=>nil,
:modifier=>"const",
:c_calling_convention=>"__stdcall",
:contains_ptr? => true,
:args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false}
],
:args_string=>"int Trinity, unsigned int* Neo",
:args_call=>"Trinity, Neo" }
assert_equal(expected, @parser.parse_declaration(@test_project, source))
end
it "extract and return function declarations inside namespace and class" do
source = "int Foo(int a, unsigned int b)"
expected = { :var_arg=>nil,
:name=>"ns1_ns2_Bar_Foo",
:unscoped_name=>"Foo",
:class=>"Bar",
:namespace=>["ns1", "ns2"],
:return=>{ :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned int", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"int a, unsigned int b",
:args_call=>"a, b" }
assert_equal(expected, @parser.parse_declaration(@test_project, source, ["ns1", "ns2"], "Bar"))
end
it "fully parse multiple prototypes" do
source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" +
"int Morpheus(int, unsigned int*);\n"
expected = [{ :var_arg=>nil,
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => true,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"TheMatrix",
:unscoped_name=>"TheMatrix",
:namespace=>[],
:class=>nil,
:modifier=>"const",
:contains_ptr? => true,
:args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false}
],
:args_string=>"int Trinity, unsigned int* Neo",
:args_call=>"Trinity, Neo" },
{ :var_arg=>nil,
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"Morpheus",
:unscoped_name=>"Morpheus",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=>"int", :name=>"cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned int*", :name=>"cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false}
],
:args_string=>"int cmock_arg1, unsigned int* cmock_arg2",
:args_call=>"cmock_arg1, cmock_arg2"
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "not extract for mocking multiply defined prototypes" do
source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" +
"const int TheMatrix(int, unsigned int*);\n"
expected = [{ :var_arg=>nil,
:name=>"TheMatrix",
:unscoped_name=>"TheMatrix",
:namespace=>[],
:class=>nil,
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => true,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:modifier=>"const",
:contains_ptr? => true,
:args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false}
],
:args_string=>"int Trinity, unsigned int* Neo",
:args_call=>"Trinity, Neo"
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "should properly handle const before return type" do
sources = [
"const int * PorkRoast(void);\n",
"const int* PorkRoast(void);\n",
"const int *PorkRoast(void);\n"
]
expected = [{ :var_arg => nil,
:name => "PorkRoast",
:unscoped_name => "PorkRoast",
:namespace=>[],
:class=>nil,
:return => { :type => "const int*",
:name => 'cmock_to_return',
:ptr? => true,
:const? => true,
:const_ptr? => false,
:str => "const int* cmock_to_return",
:void? => false
},
:modifier => "",
:contains_ptr? => false,
:args => [],
:args_string => "void",
:args_call => ""
}]
sources.each do |source|
assert_equal(expected, @parser.parse("module", source)[:functions])
end
end
it "should properly handle const before return type" do
sources = [
"int const * PorkRoast(void);\n",
"int const* PorkRoast(void);\n",
"int const *PorkRoast(void);\n"
]
expected = [{ :var_arg => nil,
:name => "PorkRoast",
:unscoped_name => "PorkRoast",
:namespace=>[],
:class=>nil,
:return => { :type => "int const*",
:name => 'cmock_to_return',
:ptr? => true,
:const? => true,
:const_ptr? => false,
:str => "int const* cmock_to_return",
:void? => false
},
:modifier => "",
:contains_ptr? => false,
:args => [],
:args_string => "void",
:args_call => ""
}]
sources.each do |source|
assert_equal(expected, @parser.parse("module", source)[:functions])
end
end
it "should properly handle const applied after asterisk in return type (not legal C, but sometimes used)" do
source = "int * const PorkRoast(void);\n"
expected = [{ :var_arg=>nil,
:name=>"PorkRoast",
:unscoped_name=>"PorkRoast",
:namespace=>[],
:class=>nil,
:return=> { :type => "int*",
:name => 'cmock_to_return',
:ptr? => true,
:const? => false,
:const_ptr? => true,
:str => "int* cmock_to_return",
:void? => false
},
:modifier=>"const",
:contains_ptr? => false,
:args=>[],
:args_string=>"void",
:args_call=>""
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "properly parse const and pointer argument types with no arg names" do
source = "void foo(int const*, int*const, const int*, const int*const, int const*const, int*, int, const int);\n"
expected = [{ :name => "foo",
:unscoped_name => "foo",
:namespace=>[],
:class=>nil,
:modifier => "",
:return => { :type => "void",
:name => "cmock_to_return",
:str => "void cmock_to_return",
:void? => true,
:ptr? => false,
:const? => false,
:const_ptr? => false
},
:var_arg => nil,
:args_string => "int const* cmock_arg1, int* const cmock_arg2, const int* cmock_arg3, const int* const cmock_arg4, " +
"int const* const cmock_arg5, int* cmock_arg6, int cmock_arg7, const int cmock_arg8",
:args => [{ :type=>"int const*", :name => "cmock_arg1", :ptr? => true, :const? => true, :const_ptr? => false },
{ :type=>"int*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => true },
{ :type=>"const int*", :name => "cmock_arg3", :ptr? => true, :const? => true, :const_ptr? => false },
{ :type=>"const int*", :name => "cmock_arg4", :ptr? => true, :const? => true, :const_ptr? => true },
{ :type=>"int const*", :name => "cmock_arg5", :ptr? => true, :const? => true, :const_ptr? => true },
{ :type=>"int*", :name => "cmock_arg6", :ptr? => true, :const? => false, :const_ptr? => false },
{ :type=>"int", :name => "cmock_arg7", :ptr? => false, :const? => false, :const_ptr? => false },
{ :type=>"int", :name => "cmock_arg8", :ptr? => false, :const? => true, :const_ptr? => false }],
:args_call => "cmock_arg1, cmock_arg2, cmock_arg3, cmock_arg4, cmock_arg5, cmock_arg6, cmock_arg7, cmock_arg8",
:contains_ptr? => true
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "properly parse const and pointer argument types with arg names" do
source = "void bar(int const* param1, int*const param2, const int* param3, const int*const param4,\n" +
" int const*const param5, int*param6, int param7, const int param8);\n"
expected = [{ :name => "bar",
:unscoped_name => "bar",
:namespace=>[],
:class=>nil,
:modifier => "",
:return => { :type => "void",
:name => "cmock_to_return",
:str => "void cmock_to_return",
:void? => true,
:ptr? => false,
:const? => false,
:const_ptr? => false
},
:var_arg => nil,
:args_string => "int const* param1, int* const param2, const int* param3, const int* const param4, " +
"int const* const param5, int* param6, int param7, const int param8",
:args => [{ :type=>"int const*", :name => "param1", :ptr? => true, :const? => true, :const_ptr? => false },
{ :type=>"int*", :name => "param2", :ptr? => true, :const? => false, :const_ptr? => true },
{ :type=>"const int*", :name => "param3", :ptr? => true, :const? => true, :const_ptr? => false },
{ :type=>"const int*", :name => "param4", :ptr? => true, :const? => true, :const_ptr? => true },
{ :type=>"int const*", :name => "param5", :ptr? => true, :const? => true, :const_ptr? => true },
{ :type=>"int*", :name => "param6", :ptr? => true, :const? => false, :const_ptr? => false },
{ :type=>"int", :name => "param7", :ptr? => false, :const? => false, :const_ptr? => false },
{ :type=>"int", :name => "param8", :ptr? => false, :const? => true, :const_ptr? => false }],
:args_call => "param1, param2, param3, param4, param5, param6, param7, param8",
:contains_ptr? => true
}].freeze
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "converts typedef'd array arguments to pointers" do
source = "Book AddToBook(Book book, const IntArray values);\n"
expected = [{ :name => "AddToBook",
:unscoped_name => "AddToBook",
:namespace=>[],
:class=>nil,
:modifier=>"",
:return => { :type => "Book",
:name => "cmock_to_return",
:str => "Book cmock_to_return",
:void? => false,
:ptr? => false,
:const? => false,
:const_ptr? => false
},
:var_arg => nil,
:args => [{ :type => "Page*", :name => "book", :ptr? => true, :const? => false, :const_ptr? => false },
{ :type => "const int*", :name => "values", :ptr? => true, :const? => true, :const_ptr? => false }],
:args_string => "Book book, const IntArray values",
:args_call => "book, values",
:contains_ptr? => true
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "properly detect typedef'd variants of void and use those" do
source = "typedef (void) FUNKY_VOID_T;\n" +
"typedef void CHUNKY_VOID_T;\n" +
"FUNKY_VOID_T DrHorrible(int SingAlong);\n" +
"int CaptainHammer(CHUNKY_VOID_T);\n"
expected = [{ :var_arg=>nil,
:name=>"DrHorrible",
:unscoped_name=>"DrHorrible",
:namespace=>[],
:class=>nil,
:return => { :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"int", :name=>"SingAlong", :ptr? => false, :const? => false, :const_ptr? => false} ],
:args_string=>"int SingAlong",
:args_call=>"SingAlong"
},
{ :var_arg=>nil,
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"CaptainHammer",
:unscoped_name=>"CaptainHammer",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ ],
:args_string=>"void",
:args_call=>""
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "be ok with structs inside of function declarations" do
source = "int DrHorrible(struct SingAlong Blog);\n" +
"void Penny(struct const _KeepYourHeadUp_ * const BillyBuddy);\n" +
"struct TheseArentTheHammer CaptainHammer(void);\n"
expected = [{ :var_arg=>nil,
:return =>{ :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"DrHorrible",
:unscoped_name=>"DrHorrible",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"struct SingAlong", :name=>"Blog", :ptr? => false, :const? => false, :const_ptr? => false} ],
:args_string=>"struct SingAlong Blog",
:args_call=>"Blog"
},
{ :var_arg=>nil,
:return=> { :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"Penny",
:unscoped_name=>"Penny",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=>"struct const _KeepYourHeadUp_*", :name=>"BillyBuddy", :ptr? => true, :const? => true, :const_ptr? => true} ],
:args_string=>"struct const _KeepYourHeadUp_* const BillyBuddy",
:args_call=>"BillyBuddy"
},
{ :var_arg=>nil,
:return=> { :type => "struct TheseArentTheHammer",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "struct TheseArentTheHammer cmock_to_return",
:void? => false
},
:name=>"CaptainHammer",
:unscoped_name=>"CaptainHammer",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ ],
:args_string=>"void",
:args_call=>""
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "extract functions containing unions with union specifier" do
source = "void OrangePeel(union STARS_AND_STRIPES * a, union AFL_CIO b)"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"OrangePeel",
:unscoped_name=>"OrangePeel",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=>"union STARS_AND_STRIPES*", :name=>"a", :ptr? => true, :const? => false, :const_ptr? => false},
{:type=>"union AFL_CIO", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"union STARS_AND_STRIPES* a, union AFL_CIO b",
:args_call=>"a, b" }]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
end
it "not be thwarted by variables named with primitive types as part of the name" do
source = "void ApplePeel(const unsigned int const_param, int int_param, int integer, char character, int* const constant)"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"ApplePeel",
:unscoped_name=>"ApplePeel",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=> "unsigned int", :name=>"const_param", :ptr? => false, :const? => true, :const_ptr? => false},
{:type=>"int", :name=>"int_param", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int", :name=>"integer", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"char", :name=>"character", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int*", :name=>"constant", :ptr? => true, :const? => false, :const_ptr? => true}
],
:args_string=>"const unsigned int const_param, int int_param, int integer, char character, int* const constant",
:args_call=>"const_param, int_param, integer, character, constant" }]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
end
it "not be thwarted by custom types named similarly to primitive types" do
source = "void LemonPeel(integer param, character thing, longint * junk, constant value, int32_t const number)"
expected = [{:var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"LemonPeel",
:unscoped_name=>"LemonPeel",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=>"integer", :name=>"param", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"character", :name=>"thing", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"longint*", :name=>"junk", :ptr? => true, :const? => false, :const_ptr? => false},
{:type=>"constant", :name=>"value", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int32_t", :name=>"number", :ptr? => false, :const? => true, :const_ptr? => false}
],
:args_string=>"integer param, character thing, longint* junk, constant value, int32_t const number",
:args_call=>"param, thing, junk, value, number" }]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
end
it "handle some of those chains of C name specifiers naturally" do
source = "void CoinOperated(signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law)"
expected = [{:var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"CoinOperated",
:unscoped_name=>"CoinOperated",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"signed char", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned long int", :name=>"xyz_123", :ptr? => false, :const? => true, :const_ptr? => false},
{:type=>"unsigned int", :name=>"abc_123", :ptr? => false, :const? => true, :const_ptr? => false},
{:type=>"long long", :name=>"arm_of_the_law", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law",
:args_call=>"abc, xyz_123, abc_123, arm_of_the_law" }]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
end
it "handle custom types of various formats" do
source = "void CardOperated(CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const * const abc123)"
expected = [{:var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"CardOperated",
:unscoped_name=>"CardOperated",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=>"CUSTOM_TYPE", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"CUSTOM_TYPE*", :name=>"xyz_123", :ptr? => true, :const? => false, :const_ptr? => false},
{:type=>"CUSTOM_TYPE", :name=>"abcxyz", :ptr? => false, :const? => true, :const_ptr? => false},
{:type=>"struct CUSTOM_TYPE const*", :name=>"abc123", :ptr? => true, :const? => true, :const_ptr? => true}
],
:args_string=>"CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const* const abc123",
:args_call=>"abc, xyz_123, abcxyz, abc123" }]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
end
it "handle arrays and treat them as pointers or strings" do
source = 'void KeyOperated(CUSTOM_TYPE thing1[], int thing2 [ ], ' \
'char thing3 [][2 ][ 3], int* thing4[4], u8 thing5[((u8)((5 + 5*2)/3))])'
expected_args = [
{ type: 'CUSTOM_TYPE*', name: 'thing1', ptr?: true, const?: false, const_ptr?: false },
{ type: 'int*', name: 'thing2', ptr?: true, const?: false, const_ptr?: false },
# this one will likely change in the future when we improve multidimensional array support
{ type: 'char*', name: 'thing3', ptr?: false, const?: false, const_ptr?: false },
# this one will likely change in the future when we improve multidimensional array support
{ type: 'int**', name: 'thing4', ptr?: true, const?: false, const_ptr?: false },
{ type: 'u8*', name: 'thing5', ptr?: true, const?: false, const_ptr?: false }
]
expected = [{:var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"KeyOperated",
:unscoped_name=>"KeyOperated",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args => expected_args,
:args_string => 'CUSTOM_TYPE* thing1, int* thing2, ' \
'char* thing3, int** thing4, u8* thing5',
:args_call => 'thing1, thing2, thing3, thing4, thing5' }]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
end
it "give a reasonable guess when dealing with weird combinations of custom types and modifiers" do
source = "void Cheese(unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq)"
expected = [{:var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"Cheese",
:unscoped_name=>"Cheese",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"unsigned CUSTOM_TYPE", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"unsigned", :name=>"xyz", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"CUSTOM_TYPE1 CUSTOM_TYPE2", :name=>"pdq", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq",
:args_call=>"abc, xyz, pdq" }]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
end
it "extract functions containing a function pointer" do
source = "void FunkyTurkey(unsigned int (*func_ptr)(int, char))"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyTurkey",
:unscoped_name=>"FunkyTurkey",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"cmock_module_func_ptr1 func_ptr",
:args_call=>"func_ptr" }]
typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions using a function pointer with shorthand notation" do
source = "void FunkyTurkey(unsigned int func_ptr(int, char))"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyTurkey",
:unscoped_name=>"FunkyTurkey",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"cmock_module_func_ptr1 func_ptr",
:args_call=>"func_ptr" }]
typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions containing a function pointer with a void" do
source = "void FunkyTurkey(void (*func_ptr)(void))"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyTurkey",
:unscoped_name=>"FunkyTurkey",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"cmock_module_func_ptr1 func_ptr",
:args_call=>"func_ptr" }]
typedefs = ["typedef void(*cmock_module_func_ptr1)(void);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions containing a function pointer with an implied void" do
source = "void FunkyTurkey(unsigned int (*func_ptr)())"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyTurkey",
:unscoped_name=>"FunkyTurkey",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"cmock_module_func_ptr1 func_ptr",
:args_call=>"func_ptr" }]
typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)();"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions containing a constant function pointer and a pointer in the nested arg list" do
source = "void FunkyChicken(unsigned int (* const func_ptr)(unsigned long int * , char))"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyChicken",
:unscoped_name=>"FunkyChicken",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => true, :const_ptr? => false}
],
:args_string=>"cmock_module_func_ptr1 const func_ptr",
:args_call=>"func_ptr" }]
typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(unsigned long int* , char);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
# it "extract functions containing a function pointer taking a vararg" do
# source = "void FunkyParrot(unsigned int (*func_ptr)(int, char, ...))"
# expected = [{ :var_arg=>nil,
# :return=>{ :type => "void",
# :name => 'cmock_to_return',
# :ptr? => false,
# :const? => false,
# :const_ptr? => false,
# :str => "void cmock_to_return",
# :void? => true
# },
# :name=>"FunkyParrot",
# :unscoped_name=>"FunkyParrot",
# :namespace=>[],
# :class=>nil,
# :modifier=>"",
# :contains_ptr? => false,
# :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false}
# ],
# :args_string=>"cmock_module_func_ptr1 func_ptr",
# :args_call=>"func_ptr" }]
# typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char, ...);"]
# result = @parser.parse("module", source)
# assert_equal(expected, result[:functions])
# assert_equal(typedefs, result[:typedefs])
# end
it "extract functions containing a function pointer with extra parenthesis and two sets" do
source = "void FunkyBudgie(int (((* func_ptr1)(int, char))), void (*func_ptr2)(void))"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyBudgie",
:unscoped_name=>"FunkyBudgie",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"cmock_module_func_ptr2", :name=>"func_ptr2", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"cmock_module_func_ptr1 func_ptr1, cmock_module_func_ptr2 func_ptr2",
:args_call=>"func_ptr1, func_ptr2" }]
typedefs = ["typedef int(*cmock_module_func_ptr1)(int, char);", "typedef void(*cmock_module_func_ptr2)(void);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions containing a function pointers, structs and other things" do
source = "struct mytype *FunkyRobin(uint16_t num1, uint16_t num2, void (*func_ptr1)(uint16_t num3, struct mytype2 *s));"
expected = [{ :var_arg=>nil,
:return=>{ :type => "struct mytype*",
:name => 'cmock_to_return',
:ptr? => true,
:const? => false,
:const_ptr? => false,
:str => "struct mytype* cmock_to_return",
:void? => false
},
:name=>"FunkyRobin",
:unscoped_name=>"FunkyRobin",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"uint16_t", :name=>"num1", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"uint16_t", :name=>"num2", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"cmock_module_func_ptr1", :name=>"func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"uint16_t num1, uint16_t num2, cmock_module_func_ptr1 func_ptr1",
:args_call=>"num1, num2, func_ptr1" }]
typedefs = ["typedef void(*cmock_module_func_ptr1)(uint16_t num3, struct mytype2* s);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions containing an anonymous function pointer" do
source = "void FunkyFowl(unsigned int (* const)(int, char))"
expected = [{ :var_arg=>nil,
:return=>{ :type => "void",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "void cmock_to_return",
:void? => true
},
:name=>"FunkyFowl",
:unscoped_name=>"FunkyFowl",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"cmock_module_func_ptr1", :name=>"cmock_arg1", :ptr? => false, :const? => true, :const_ptr? => false}
],
:args_string=>"cmock_module_func_ptr1 const cmock_arg1",
:args_call=>"cmock_arg1" }]
typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions returning a function pointer" do
source = "unsigned short (*FunkyPidgeon( const char op_code ))( int, long int )"
expected = [{ :var_arg=>nil,
:return=>{ :type => "cmock_module_func_ptr1",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "cmock_module_func_ptr1 cmock_to_return",
:void? => false
},
:name=>"FunkyPidgeon",
:unscoped_name=>"FunkyPidgeon",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"char", :name=>"op_code", :ptr? => false, :const? => true, :const_ptr? => false}
],
:args_string=>"const char op_code",
:args_call=>"op_code" }]
typedefs = ["typedef unsigned short(*cmock_module_func_ptr1)( int, long int );"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions returning a function pointer with implied void" do
source = "unsigned short (*FunkyTweetie())()"
expected = [{ :var_arg=>nil,
:return=>{ :type => "cmock_module_func_ptr1",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "cmock_module_func_ptr1 cmock_to_return",
:void? => false
},
:name=>"FunkyTweetie",
:unscoped_name=>"FunkyTweetie",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[],
:args_string=>"void",
:args_call=>"" }]
typedefs = ["typedef unsigned short(*cmock_module_func_ptr1)();"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions returning a function pointer where everything is a void" do
source = "void (* FunkySeaGull(void))(void)"
expected = [{ :var_arg=>nil,
:return=>{ :type => "cmock_module_func_ptr1",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "cmock_module_func_ptr1 cmock_to_return",
:void? => false
},
:name=>"FunkySeaGull",
:unscoped_name=>"FunkySeaGull",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[],
:args_string=>"void",
:args_call=>"" }]
typedefs = ["typedef void(*cmock_module_func_ptr1)(void);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions returning a function pointer with some pointer nonsense" do
source = "unsigned int * (* FunkyMacaw(double* foo, THING *bar))(unsigned int)"
expected = [{ :var_arg=>nil,
:return=>{ :type => "cmock_module_func_ptr1",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "cmock_module_func_ptr1 cmock_to_return",
:void? => false
},
:name=>"FunkyMacaw",
:unscoped_name=>"FunkyMacaw",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=>"double*", :name=>"foo", :ptr? => true, :const? => false, :const_ptr? => false},
{:type=>"THING*", :name=>"bar", :ptr? => true, :const? => false, :const_ptr? => false}
],
:args_string=>"double* foo, THING* bar",
:args_call=>"foo, bar" }]
typedefs = ["typedef unsigned int *(*cmock_module_func_ptr1)(unsigned int);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract this SQLite3 function with an anonymous function pointer arg (regression test)" do
source = "SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*))"
expected = [{ :var_arg=>nil,
:return=>{ :type => "int",
:name => "cmock_to_return",
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"sqlite3_bind_text",
:unscoped_name=>"sqlite3_bind_text",
:namespace=>[],
:class=>nil,
:modifier=>"SQLITE_API",
:contains_ptr? => true,
:args=>[ {:type=>"sqlite3_stmt*", :name=>"cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false},
{:type=>"int", :name=>"cmock_arg3", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"const char*", :name=>"cmock_arg4", :ptr? => false, :const? => true, :const_ptr? => false},
{:type=>"int", :name=>"n", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"cmock_module_func_ptr1", :name=>"cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"sqlite3_stmt* cmock_arg2, int cmock_arg3, const char* cmock_arg4, int n, cmock_module_func_ptr1 cmock_arg1",
:args_call=>"cmock_arg2, cmock_arg3, cmock_arg4, n, cmock_arg1" }]
typedefs = ["typedef void(*cmock_module_func_ptr1)(void*);"]
result = @parser.parse("module", source)
assert_equal(expected, result[:functions])
assert_equal(typedefs, result[:typedefs])
end
it "extract functions with varargs" do
source = "int XFiles(int Scully, int Mulder, ...);\n"
expected = [{ :var_arg=>"...",
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"XFiles",
:unscoped_name=>"XFiles",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"int", :name=>"Scully", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int", :name=>"Mulder", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"int Scully, int Mulder",
:args_call=>"Scully, Mulder"
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "extract functions with void pointers" do
source = "void* MoreSillySongs(void* stuff);\n"
expected = [{ :var_arg=>nil,
:return=> { :type => "void*",
:name => 'cmock_to_return',
:ptr? => true,
:const? => false,
:const_ptr? => false,
:str => "void* cmock_to_return",
:void? => false
},
:name=>"MoreSillySongs",
:unscoped_name=>"MoreSillySongs",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => true,
:args=>[ {:type=>"void*", :name=>"stuff", :ptr? => true, :const? => false, :const_ptr? => false}
],
:args_string=>"void* stuff",
:args_call=>"stuff"
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "extract functions with strippable confusing junk like gcc attributes" do
source = "int LaverneAndShirley(int Lenny, int Squiggy) __attribute__((weak)) __attribute__ ((deprecated));\n"
expected = [{ :var_arg=>nil,
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"LaverneAndShirley",
:unscoped_name=>"LaverneAndShirley",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"int", :name=>"Lenny", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int", :name=>"Squiggy", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"int Lenny, int Squiggy",
:args_call=>"Lenny, Squiggy"
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "extract functions with strippable confusing junk like gcc attributes with parenthesis" do
source = "int TheCosbyShow(int Cliff, int Claire) __attribute__((weak, alias (\"__f\"));\n"
expected = [{ :var_arg=>nil,
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"TheCosbyShow",
:unscoped_name=>"TheCosbyShow",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"int", :name=>"Cliff", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int", :name=>"Claire", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"int Cliff, int Claire",
:args_call=>"Cliff, Claire"
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "divines all permutations of ptr, const, and const_ptr correctly" do
truth_table = [
# argument ptr const const_ptr
[ "constNOTconst constNOTconst", false, false, false ],
[ "const constNOTconst constNOTconst", false, true, false ],
[ "constNOTconst const constNOTconst", false, true, false ],
[ "constNOTconst *constNOTconst", true, false, false ],
[ "const constNOTconst *constNOTconst", true, true, false ],
[ "constNOTconst const *constNOTconst", true, true, false ],
[ "constNOTconst *const constNOTconst", true, false, true ],
[ "const constNOTconst *const constNOTconst", true, true, true ],
[ "constNOTconst const *const constNOTconst", true, true, true ],
[ "constNOTconst **constNOTconst", true, false, false ],
[ "const constNOTconst **constNOTconst", true, false, false ],
[ "constNOTconst const **constNOTconst", true, false, false ],
[ "constNOTconst *const *constNOTconst", true, true, false ],
[ "const constNOTconst *const *constNOTconst", true, true, false ],
[ "constNOTconst const *const *constNOTconst", true, true, false ],
[ "constNOTconst **const constNOTconst", true, false, true ],
[ "const constNOTconst **const constNOTconst", true, false, true ],
[ "constNOTconst const **const constNOTconst", true, false, true ],
[ "constNOTconst *const *const constNOTconst", true, true, true ],
[ "const constNOTconst *const *const constNOTconst", true, true, true ],
[ "constNOTconst const *const *const constNOTconst", true, true, true ]
]
truth_table.each do |entry|
assert_equal(@parser.divine_ptr(entry[0]), entry[1])
assert_equal(@parser.divine_const(entry[0]), entry[2])
assert_equal(@parser.divine_ptr_and_const(entry[0]),
{ ptr?: entry[1], const?: entry[2], const_ptr?: entry[3] })
end
end
it "divines ptr correctly for string types" do
truth_table = [
# argument ptr
[ "char s", false ],
[ "const char s", false ],
[ "char const s", false ],
[ "char *s", false ],
[ "const char *s", false ],
[ "char const *s", false ],
[ "char *const s", false ],
[ "const char *const s", false ],
[ "char const *const s", false ],
[ "char **s", true ],
[ "const char **s", true ],
[ "char const **s", true ],
[ "char *const *s", true ],
[ "const char *const *s", true ],
[ "char const *const *s", true ],
[ "char **const s", true ],
[ "const char **const s", true ],
[ "char const **const s", true ],
[ "char *const *const s", true ],
[ "const char *const *const s", true ],
[ "char const *const *const s", true ]
]
truth_table.each do |entry|
assert_equal(@parser.divine_ptr(entry[0]), entry[1])
end
end
it "Transform inline functions only removes comments from a header with no inlines" do
# We remove the comments from the header, this is to protect the parser from wrongfully recognizing inline functions
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"
expected =
"#ifndef _NOINCLUDES\n" +
"#define _NOINCLUDES\n" +
"#include \"unity.h\"\n" +
"#include \"cmock.h\"\n" +
"#include \"YetAnotherHeader.h\"\n" +
"\n" +
"\n" + # Removed comment
"#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(expected, @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" +
"\n" + # Removed comment
"#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 "Transform inline functions leaves static variables" 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" +
"int my_function(int a);\n" +
"static inline int staticinlinefunc(struct my_struct *s)\n" +
"{\n" +
" return s->a;\n" +
"}\n" +
"static const int my_variable = 5;\n" +
"struct my_struct {\n" +
"int a;\n" +
"int b;\n" +
"int b;\n" +
"char c;\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" +
"\n" + #The comments are now removed
"#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" +
"int my_function(int a);\n" +
"int staticinlinefunc(struct my_struct *s);\n" +
"static const int my_variable = 5;\n" +
"struct my_struct {\n" +
"int a;\n" +
"int b;\n" +
"int b;\n" +
"char c;\n" +
"};\n" +
"#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
it "handles parsing multiline functions" do
source = "int\nLaverneAndShirley(int Lenny,\n int Squiggy);\n"
expected = [{ :var_arg=>nil,
:return=> { :type => "int",
:name => 'cmock_to_return',
:ptr? => false,
:const? => false,
:const_ptr? => false,
:str => "int cmock_to_return",
:void? => false
},
:name=>"LaverneAndShirley",
:unscoped_name=>"LaverneAndShirley",
:namespace=>[],
:class=>nil,
:modifier=>"",
:contains_ptr? => false,
:args=>[ {:type=>"int", :name=>"Lenny", :ptr? => false, :const? => false, :const_ptr? => false},
{:type=>"int", :name=>"Squiggy", :ptr? => false, :const? => false, :const_ptr? => false}
],
:args_string=>"int Lenny, int Squiggy",
:args_call=>"Lenny, Squiggy"
}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "imports C++ differently when asked" do
source =
[
"namespace ns1 {\n",
" namespace ns2 {\n",
"\n",
" class cls1 {\n",
" public:\n",
" int f_header_impl(int a, int b){\n",
" return a + b;\n",
" }\n",
"\n",
" static void f_void();\n",
" static int f_ret_simple();\n",
"\n",
" protected:\n",
" static void protected_f_void();\n",
"\n",
" public:\n",
" private:\n",
" static void private_f_void();\n",
" }; // cls1\n",
" } // ns2\n",
"} // ns1\n"
].join
expected =
[
"namespace ns1 { namespace ns2 { class cls1 { public: int f_header_impl",
"static void f_void()",
"static int f_ret_simple()",
"protected: static void protected_f_void()",
"public: private: static void private_f_void()",
"}",
"} }"
]
assert_equal(expected, @parser.import_source(source, @test_project, cpp=true))
refute_equal(expected, @parser.import_source(source, @test_project))
end
# only so parse_functions does not raise an error
def dummy_source
"void dummy(void);"
end
# Expected result of above
def dummy_func
{ :name => "dummy",
:unscoped_name => "dummy",
:class => nil,
:namespace => [],
:var_arg => nil,
:args_string => "void",
:args => [],
:args_call => "",
:contains_ptr? => false,
:modifier => "",
:return => {
:type => "void",
:name => "cmock_to_return",
:str => "void cmock_to_return",
:void? => true,
:ptr? => false,
:const? => false,
:const_ptr? => false}}
end
# Commonly used example function
def voidvoid_func(namespace=[], name="Classic_functional")
{ :name => name,
:unscoped_name => "functional",
:class => "Classic",
:namespace => namespace,
:var_arg => nil,
:args_string => "void",
:args => [],
:args_call => "",
:contains_ptr? => false,
:modifier => "",
:return => {
:type=>"void",
:name=>"cmock_to_return",
:str=>"void cmock_to_return",
:void? => true,
:ptr? => false,
:const? => false,
:const_ptr? => false}}
end
it "parses public C++ functions" do
source = dummy_source + <<~SOURCE
class Classic {
public:
static void functional(void);
};
SOURCE
expected = [dummy_func, voidvoid_func]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "handles a namespace" do
source = dummy_source + <<~SOURCE
namespace ns1 {
class Classic {
public:
static void functional(void);
};
} // ns1
SOURCE
expected = [dummy_func,
voidvoid_func(namespace=["ns1"], name="ns1_Classic_functional")]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "handles nested namespaces" do
source = dummy_source + <<~SOURCE
namespace ns1 {
namespace ns2 {
class Classic {
public:
static void functional(void);
};
} // ns1
} // ns1
SOURCE
expected = [dummy_func,
voidvoid_func(namespace=["ns1", "ns2"], name="ns1_ns2_Classic_functional")]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "ignores non-static C++ functions" do
source = dummy_source + <<~SOURCE
class Classic {
public:
void functional(void);
};
SOURCE
expected = [dummy_func]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "ignores private functions" do
source = dummy_source + <<~SOURCE
class Classic {
private:
static void functional(void);
};
SOURCE
expected = [dummy_func]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "parses public C++ functions after private functions" do
source = dummy_source + <<~SOURCE
class Classic {
private:
static void ignoreme(void);
public:
static void functional(void);
};
SOURCE
expected = [dummy_func, voidvoid_func]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "ignores protected functions" do
source = dummy_source + <<~SOURCE
class Classic {
protected:
static void functional(void);
};
SOURCE
expected = [dummy_func]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "parses public C++ functions after protected functions" do
source = dummy_source + <<~SOURCE
class Classic {
protected:
static void ignoreme(void);
public:
static void functional(void);
};
SOURCE
expected = [dummy_func, voidvoid_func]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "parses multiple classes in same file with uniquely named functions" do
source = dummy_source + <<~SOURCE
namespace ns1 {
class Classic {
public:
static void functional(void);
};
class Classical {
public:
static int functionality(int a);
};
} // ns1
class Classy {
public:
static int* func(int* a);
};
SOURCE
expected = [dummy_func,
voidvoid_func(["ns1"], name="ns1_Classic_functional"),
{ :name => "ns1_Classical_functionality",
:unscoped_name => "functionality",
:class => "Classical",
:namespace => ["ns1"],
:var_arg => nil,
:args_string => "int a",
:args => [
{ :ptr? => false,
:const? => false,
:const_ptr? => false,
:name => "a",
:type => "int"}],
:args_call => "a",
:contains_ptr? => false,
:modifier => "",
:return => {
:type=>"int",
:name=>"cmock_to_return",
:str=>"int cmock_to_return",
:void? => false,
:ptr? => false,
:const? => false,
:const_ptr? => false}},
{ :name => "Classy_func",
:unscoped_name => "func",
:class => "Classy",
:namespace => [],
:var_arg => nil,
:args_string => "int* a",
:args => [
{ :ptr? => true,
:const? => false,
:const_ptr? => false,
:name => "a",
:type => "int*"}],
:args_call => "a",
:contains_ptr? => true,
:modifier => "",
:return => {
:type=>"int*",
:name=>"cmock_to_return",
:str=>"int* cmock_to_return",
:void? => false,
:ptr? => true,
:const? => false,
:const_ptr? => false}}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "handles multiple classes in same file with identically named functions" do
source = dummy_source + <<~SOURCE
namespace ns1 {
class Classic {
public:
static void functional(void);
};
class Classical {
public:
static int functional(int a);
};
} // ns1
class Classy {
public:
static int* functional(int* a);
};
SOURCE
expected = [dummy_func,
voidvoid_func(["ns1"], name="ns1_Classic_functional"),
{ :name => "ns1_Classical_functional",
:unscoped_name => "functional",
:class => "Classical",
:namespace => ["ns1"],
:var_arg => nil,
:args_string => "int a",
:args => [
{ :ptr? => false,
:const? => false,
:const_ptr? => false,
:name => "a",
:type => "int"}],
:args_call => "a",
:contains_ptr? => false,
:modifier => "",
:return => {
:type=>"int",
:name=>"cmock_to_return",
:str=>"int cmock_to_return",
:void? => false,
:ptr? => false,
:const? => false,
:const_ptr? => false}},
{ :name => "Classy_functional",
:unscoped_name => "functional",
:class => "Classy",
:namespace => [],
:var_arg => nil,
:args_string => "int* a",
:args => [
{ :ptr? => true,
:const? => false,
:const_ptr? => false,
:name => "a",
:type => "int*"}],
:args_call => "a",
:contains_ptr? => true,
:modifier => "",
:return => {
:type=>"int*",
:name=>"cmock_to_return",
:str=>"int* cmock_to_return",
:void? => false,
:ptr? => true,
:const? => false,
:const_ptr? => false}}]
assert_equal(expected, @parser.parse("module", source)[:functions])
end
it "Transform inline functions can handle inline function declarations" do
source =
"static inline int dummy_func_decl(int a, char b, float c);\n" + # First declaration
"static inline int dummy_func_decl2(int a, char b, float c)\n\n\n\n\n\n;\n" + # Second declaration with a lot of newlines before the semicolon to mess with the parser
"static inline int staticinlinefunc(struct my_struct *s)\n" + # 'normal' inline pattern
"{\n" +
" return dummy_func_decl(1, 1, 1);\n" +
"}\n" +
"struct my_struct_with_inline_in_it\n" + # struct definition in between to mess with the parser
"{\n" +
" int a;\n" +
" char b;\n" +
" float inlineb;\n" +
"};\n" +
"static inline int dummy_func_decl(int a, char b, float c) {\n" + # Second user pattern
" return 42;\n" +
"}\n" +
"\n"
expected =
"int dummy_func_decl(int a, char b, float c);\n" +
"int dummy_func_decl2(int a, char b, float c)\n\n\n\n\n\n;\n" + # Second declaration with a lot of newlines until the semicolon to mess with the parser
"int staticinlinefunc(struct my_struct *s);\n" +
"struct my_struct_with_inline_in_it\n" +
"{\n" +
" int a;\n" +
" char b;\n" +
" float inlineb;\n" +
"};\n" +
"int dummy_func_decl(int a, char b, float c);\n" +
"\n"
@parser.treat_inlines = :include
assert_equal(expected, @parser.transform_inline_functions(source))
end
it "Transform inline functions can handle header with only inline function declarations" do
source =
"static inline int dummy_func_decl(int a, char b, float c);\n" +
"\n"
expected =
"int dummy_func_decl(int a, char b, float c);\n" +
"\n"
@parser.treat_inlines = :include
assert_equal(expected, @parser.transform_inline_functions(source))
end
it "Transform inline functions using gnu attribute notation" do
source =
"static __inline__ __attribute__ ((always_inline)) uint16_t _somefunc (uint32_t a)\n" +
"{\n" +
" return _someotherfunc (a);\n" +
"}\n" +
"static __attribute__ (( always_inline )) uint16_t _somefunc_0 (uint32_t a)\n" +
"{\n" +
" return (uint16_t) a;\n" +
"}\n" +
"\n"
expected =
"uint16_t _somefunc (uint32_t a);\n" +
"uint16_t _somefunc_0 (uint32_t a);\n" +
"\n"
@parser.treat_inlines = :include
@parser.inline_function_patterns = ['(?:static\s*)?(?:__inline__)?__attribute__\s*\([ (]*always_inline[ )]*\)', 'static __inline__']
assert_equal(expected, @parser.transform_inline_functions(source))
end
it "Transform inline functions takes user provided patterns into account" do
source =
"static __inline__ __attribute__ ((always_inline)) uint16_t _somefunc (uint32_t a)\n" +
"{\n" +
" return _someotherfunc (a);\n" +
"}\n" +
"static __inline__ uint16_t _somefunc_0 (uint32_t a)\n" +
"{\n" +
" return (uint16_t) a;\n" +
"}\n" +
"\n"
expected =
"uint16_t _somefunc (uint32_t a);\n" +
"uint16_t _somefunc_0 (uint32_t a);\n" +
"\n"
@parser.treat_inlines = :include
@parser.inline_function_patterns = ['static __inline__ __attribute__ \(\(always_inline\)\)', 'static __inline__']
assert_equal(expected, @parser.transform_inline_functions(source))
end
it "Transform inline functions limits deleting user macro to actual line/word" do
source =
"#if defined (FORCE_INLINE)\n" +
"#define MY_LIBRARY_INLINE static __inline__ __attribute__ ((always_inline))\n" +
"#else\n" +
"#define MY_LIBRARY_INLINE\n" +
"#endif\n" +
"#define INLINE static __inline__ __attribute__ ((always_inline))\n" +
"#define INLINE_TWO \\\nstatic\\\ninline\n" +
"INLINE uint16_t _somefunc (uint32_t a)\n" +
"{\n" +
" return _someotherfunc (a);\n" +
"}\n" +
"static __inline__ uint16_t _somefunc_0 (uint32_t a)\n" +
"{\n" +
" return (uint16_t) a;\n" +
"}\n" +
"static __inline__ __attribute__ \(\(always_inline\)\) uint16_t _somefunc_1 (uint32_t a)\n" +
"{\n" +
" return (uint16_t) a;\n" +
"}\n" +
"INLINE_TWO uint16_t _somefunc_2(uint32_t a)\n" +
"{\n" +
" return (uint16_t) a;\n" +
"}\n" +
"#define INLINE_THREE \\\nstatic\\\ninline"
expected =
"#if defined (FORCE_INLINE)\n" +
"#else\n" +
"#endif\n" +
"uint16_t _somefunc (uint32_t a);\n" +
"uint16_t _somefunc_0 (uint32_t a);\n" +
"uint16_t _somefunc_1 (uint32_t a);\n" +
"uint16_t _somefunc_2(uint32_t a);\n"
@parser.treat_inlines = :include
@parser.inline_function_patterns = ['MY_LIBRARY_INLINE', 'INLINE_THREE', 'INLINE_TWO', 'INLINE', 'static __inline__ __attribute__ \(\(always_inline\)\)', 'static __inline__']
assert_equal(expected, @parser.transform_inline_functions(source))
end
it "Transform inline functions will ignore comments containing static" do
source =
"typedef struct {\n" +
"const uint32_t var1;\n" +
"const uint16_t var2; // comment with the word static in it\n" +
"} struct1;\n" +
"\n" +
"typedef struct {\n" +
"const uint32_t var1;\n" +
"const uint16_t var2;\n" +
"} struct2;\n"
expected =
"typedef struct {\n" +
"const uint32_t var1;\n" +
"const uint16_t var2; \n" +
"} struct1;\n" +
"\n" +
"typedef struct {\n" +
"const uint32_t var1;\n" +
"const uint16_t var2;\n" +
"} struct2;\n"
@parser.treat_inlines = :include
assert_equal(expected, @parser.transform_inline_functions(source))
end
it "Transform macros with escapes of empty lines" do
source =
"#define some_msg_t_FIELDLIST(X, a) \\\n" +
"\n" +
"#define some_msg_t_CALLBACK NULL\n"
expected =
"#define some_msg_t_FIELDLIST(X, a) \n" +
"#define some_msg_t_CALLBACK NULL\n"
@parser.treat_inlines = :include
assert_equal(expected, @parser.transform_inline_functions(source))
end
end