mirror of
https://github.com/ThrowTheSwitch/CMock.git
synced 2026-08-06 11:17:50 +00:00
- Added unit tests for cmock_header_parser.rb
git-svn-id: http://cmock.svn.sourceforge.net/svnroot/cmock/trunk@31 bf332499-1b4d-0410-844d-d2d48d5cc64c
This commit is contained in:
+53
-37
@@ -1,35 +1,41 @@
|
||||
class CMockHeaderParser
|
||||
|
||||
attr_accessor :match_type, :attribute_match, :decl_modifier, :decl_return, :decl_function, :decl_args
|
||||
|
||||
attr_accessor :match_type, :attribute_match, :src_lines, :funcs, :c_attributes, :declaration_parse_matcher, :included
|
||||
|
||||
def initialize(source, match_type=/\w+\**/, attributes=['static', '__monitor', '__ramfunc', '__irq', '__fiq'])
|
||||
source = source.gsub(/\/\/.*$/, '') #remove line comments
|
||||
source = source.gsub(/\/\*.*?\*\//m, '') #remove block comments
|
||||
@lines = source.split(/(^\s*\#.*$) # Treat preprocessor directives as a logical line
|
||||
| (;|\{|\}) /x) # Match ;, {, and } as end of lines
|
||||
@lines.delete_if {|line| line =~ /\\\n/} #ignore lines that contain continuation lines
|
||||
@lines.delete_if {|line| line =~ /typedef/} #ignore lines that contain typedef statements
|
||||
@lines.delete_if {|line| line =~ /\#define/} #remove defines
|
||||
|
||||
@functions = nil
|
||||
import_source(source)
|
||||
@funcs = nil
|
||||
@match_type = match_type
|
||||
@c_attributes = attributes
|
||||
@declaration_parse_matcher = /(\w*\s+)*([^\s]+)\s+(\w+)\s*\(([^\)]*)\)/
|
||||
@included = nil
|
||||
@var_args_ellipsis = '...'
|
||||
end
|
||||
|
||||
def parse
|
||||
mod = {:includes => nil, :externs => nil, :functions => []}
|
||||
mod[:includes] = included_files
|
||||
mod[:externs] = externs
|
||||
functions.each do |decl|
|
||||
mod[:functions] << parse_declaration(decl)
|
||||
parse_functions
|
||||
if !@funcs.nil? and @funcs.length > 0
|
||||
@funcs.each do |decl|
|
||||
mod[:functions] << parse_declaration(decl)
|
||||
end
|
||||
end
|
||||
return mod
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def import_source(source)
|
||||
source = source.gsub(/\/\/.*$/, '') #remove line comments
|
||||
source = source.gsub(/\/\*.*?\*\//m, '') #remove block comments
|
||||
@src_lines = source.split(/(^\s*\#.*$) # Treat preprocessor directives as a logical line
|
||||
| (;|\{|\}) /x) # Match ;, {, and } as end of lines
|
||||
@src_lines.delete_if {|line| line.length < 1}
|
||||
@src_lines.delete_if {|line| line =~ /\\\n/} #ignore lines that contain continuation lines
|
||||
@src_lines.delete_if {|line| line =~ /typedef/i} #ignore lines that contain typedef statements
|
||||
@src_lines.delete_if {|line| line =~ /\#define/i} #remove defines
|
||||
end
|
||||
|
||||
def c_attributes=(value)
|
||||
@c_attributes = value
|
||||
@@ -39,9 +45,11 @@ class CMockHeaderParser
|
||||
def included_files
|
||||
if @included.nil?
|
||||
@included = []
|
||||
@lines.each do |line|
|
||||
if line =~ /#include\s+"(.*)"/
|
||||
@included << $1
|
||||
if !@src_lines.nil? and @src_lines.length > 0
|
||||
@src_lines.each do |line|
|
||||
if line =~ /#include\s+"(.*)"/
|
||||
@included << $1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -52,28 +60,30 @@ class CMockHeaderParser
|
||||
if !@externs
|
||||
@externs = []
|
||||
depth = 0
|
||||
@lines.each do |line|
|
||||
if depth.zero? && line =~ /^\s*extern.*/m
|
||||
@externs << $&.strip.gsub(/\s+/, ' ')
|
||||
end
|
||||
if line =~ /\{/
|
||||
depth += 1
|
||||
end
|
||||
if line =~ /\}/
|
||||
depth -= 1
|
||||
if !@src_lines.nil? and @src_lines.length > 0
|
||||
@src_lines.each do |line|
|
||||
if depth.zero? && line =~ /^\s*extern.*/m
|
||||
@externs << $&.strip.gsub(/\s+/, ' ')
|
||||
end
|
||||
if line =~ /\{/
|
||||
depth += 1
|
||||
end
|
||||
if line =~ /\}/
|
||||
depth -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return @externs
|
||||
end
|
||||
|
||||
def functions
|
||||
if @functions.nil?
|
||||
@functions = []
|
||||
def parse_functions
|
||||
if @funcs.nil?
|
||||
@funcs = []
|
||||
depth = 0
|
||||
@lines.each do |line|
|
||||
@src_lines.each do |line|
|
||||
if depth.zero? && line =~ /#{@attribute_match}\s*#{@match_type}\s+\w+\s*\(.*\)/m
|
||||
@functions << line.strip.gsub(/\s+/, ' ')
|
||||
@funcs << line.strip.gsub(/\s+/, ' ')
|
||||
end
|
||||
if line =~ /\{/
|
||||
depth += 1
|
||||
@@ -84,21 +94,21 @@ class CMockHeaderParser
|
||||
end
|
||||
end
|
||||
#eliminate define functions
|
||||
@functions ||= functions.reject do |func|
|
||||
@funcs ||= @funcs.reject do |func|
|
||||
func =~ /\bdefine\b/
|
||||
end
|
||||
return @functions
|
||||
return @funcs
|
||||
end
|
||||
|
||||
def parse_args(arg_list)
|
||||
args = []
|
||||
arg_list.split(',').each do |arg|
|
||||
arg = arg.strip
|
||||
return args if ((arg == @var_args_ellipsis) || (arg == 'void'))
|
||||
return args if ((arg == '...') || (arg == 'void'))
|
||||
arg_match = arg.match /^(.+)\s+(\*?\w+)$/
|
||||
raise "Failed parsing argument list at argument: '#{arg}'" if arg_match.nil?
|
||||
|
||||
#put the asterix with the type (where it belongs)
|
||||
#put the asterisk with the type (where it belongs)
|
||||
if (arg_match[-1][0] == '*')
|
||||
arg_match[1] << '*'
|
||||
arg_match[-1].slice!(0)
|
||||
@@ -119,15 +129,21 @@ class CMockHeaderParser
|
||||
decl[:modifier] = modifier.strip
|
||||
decl[:rettype] = $2
|
||||
decl[:name] = $3
|
||||
|
||||
args = $4
|
||||
|
||||
#put the asterisk with the type (where it belongs)
|
||||
if (decl[:name][0] == '*')
|
||||
decl[:rettype] << '*'
|
||||
decl[:name].slice!(0)
|
||||
end
|
||||
|
||||
#remove default parameter statements from mock definitions
|
||||
args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*\,/, ',')
|
||||
args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ')
|
||||
|
||||
#check for var args
|
||||
if (args =~ /\.\.\./)
|
||||
decl[:var_arg] = args.match( /[\w\s]*\.\.\./ ).to_s
|
||||
decl[:var_arg] = args.match( /[\w\s]*\.\.\./ ).to_s.strip
|
||||
if (args =~ /\,[\w\s]*\.\.\./)
|
||||
args = args.gsub!(/\,[\w\s]*\.\.\./,'')
|
||||
else
|
||||
|
||||
+2
-3
@@ -88,9 +88,8 @@ module RakefileHelpers
|
||||
|
||||
def execute(command_string, verbose=true)
|
||||
report command_string
|
||||
output = `#{command_string}`
|
||||
report(output) if verbose
|
||||
report ''
|
||||
output = `#{command_string}`.chomp
|
||||
report(output) if (verbose && !output.nil? && (output.length > 0))
|
||||
if $?.exitstatus != 0
|
||||
raise "Command failed. (Returned #{$?.exitstatus})"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
require File.expand_path(File.dirname(__FILE__)) + "/../test_helper"
|
||||
require File.expand_path(File.dirname(__FILE__)) + "/../../lib/cmock_header_parser"
|
||||
|
||||
class CMockHeaderParserTest < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@parser = CMockHeaderParser.new("//some contents")
|
||||
end
|
||||
|
||||
def teardown
|
||||
end
|
||||
|
||||
should "create and initialize variables to defaults appropriately" do
|
||||
@parser = CMockHeaderParser.new("//some contents")
|
||||
assert_nil(@parser.funcs)
|
||||
assert_equal(/\w+\**/, @parser.match_type)
|
||||
assert_equal(['static', '__monitor', '__ramfunc', '__irq', '__fiq'], @parser.c_attributes)
|
||||
assert_equal(/(\w*\s+)*([^\s]+)\s+(\w+)\s*\(([^\)]*)\)/, @parser.declaration_parse_matcher)
|
||||
assert_nil(@parser.included)
|
||||
end
|
||||
|
||||
should "strip out line comments" do
|
||||
source =
|
||||
" abcd;\n" +
|
||||
"// hello;\n" +
|
||||
"who // is you\n"
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
" abcd",
|
||||
";",
|
||||
"\n\nwho \n"
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "remove block comments" do
|
||||
source =
|
||||
" abcd;\n" +
|
||||
"/* hello;*/\n" +
|
||||
"who /* is you\n" +
|
||||
"whatdya say? */\n" +
|
||||
"/* shizzzle*/"
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
" abcd",
|
||||
";",
|
||||
"\n\nwho \n"
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "treat preprocessor directives as single line" do
|
||||
#flunk "this substitution generates an empty first line element?"
|
||||
source =
|
||||
"#when stuff_happens\n" +
|
||||
"#ifdef _TEST\n" +
|
||||
"#pragma stack_switch"
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
"#when stuff_happens",
|
||||
"\n",
|
||||
"#ifdef _TEST",
|
||||
"\n",
|
||||
"#pragma stack_switch"
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "Match ; { and } as end of line characters" do
|
||||
source =
|
||||
" i like ice cream; and i can eat { vanilla, chocolate } when I want to; so there!"
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
" i like ice cream",
|
||||
";",
|
||||
" and i can eat ",
|
||||
"{",
|
||||
" vanilla, chocolate ",
|
||||
"}",
|
||||
" when I want to",
|
||||
";",
|
||||
" so there!"
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "ignore lines that contain continuation characters" do
|
||||
source =
|
||||
"hoo hah \\\n" +
|
||||
"when \\ \n"
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "ignore lines that contain typedef statements" do
|
||||
source =
|
||||
"#typedef uint32 (unsigned int)\n" +
|
||||
"whack me? #typedef int INT\n" +
|
||||
"#typedef who cares what really comes here\n" +
|
||||
"this should remain!"
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
"\nthis should remain!"
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "remove defines" do
|
||||
source =
|
||||
"hello;\n" +
|
||||
"#define whatever you feel like defining\n" +
|
||||
"#DEFINE I JUST DON'T CARE\n" +
|
||||
"#deFINE\n"
|
||||
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
"hello",
|
||||
";",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "extract and return included files" do
|
||||
source =
|
||||
"int Foo(int a, unsigned int b);" +
|
||||
"hello;\n" +
|
||||
"#define whatever you feel like defining\n" +
|
||||
"#include \"myheader.h\"\n" +
|
||||
"#include \t \"os.h\"\n"
|
||||
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
parsed_stuff = @parser.parse
|
||||
|
||||
expected =
|
||||
[
|
||||
"myheader.h",
|
||||
"os.h"
|
||||
]
|
||||
|
||||
assert_equal(expected, parsed_stuff[:includes])
|
||||
end
|
||||
|
||||
should "extract and return externs" do
|
||||
source =
|
||||
"extern int Foo(int a, unsigned int b);\n" +
|
||||
"extern unsigned int NumSamples;\n" +
|
||||
"extern FOO_TYPE fooFun;"
|
||||
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
|
||||
expected =
|
||||
[
|
||||
"extern int Foo(int a, unsigned int b)",
|
||||
";",
|
||||
"\nextern unsigned int NumSamples",
|
||||
";",
|
||||
"\nextern FOO_TYPE fooFun",
|
||||
";"
|
||||
]
|
||||
|
||||
assert_equal(expected, @parser.src_lines)
|
||||
end
|
||||
|
||||
should "extract and return function declarations" do
|
||||
|
||||
source =
|
||||
"int Foo(int a, unsigned int b);\n" +
|
||||
"void bar \n(uint la, int de, bool da);\n" +
|
||||
"void\n shiz(void);\n" +
|
||||
"void tat();\n"
|
||||
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
parsed_stuff = @parser.parse
|
||||
|
||||
expected =
|
||||
[
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "int a, unsigned int b",
|
||||
:rettype => "int",
|
||||
:var_arg => nil,
|
||||
:args => [{:type => "int", :name => "a"}, {:type => "unsigned int", :name => "b"}],
|
||||
:name => "Foo"
|
||||
},
|
||||
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "uint la, int de, bool da",
|
||||
:rettype => "void",
|
||||
:var_arg => nil,
|
||||
:args =>
|
||||
[
|
||||
{:type => "uint", :name => "la"},
|
||||
{:type => "int", :name => "de"},
|
||||
{:type => "bool", :name => "da"}
|
||||
],
|
||||
:name => "bar"
|
||||
},
|
||||
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "void",
|
||||
:rettype => "void",
|
||||
:var_arg => nil,
|
||||
:args => [],
|
||||
:name => "shiz"
|
||||
},
|
||||
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "",
|
||||
:rettype => "void",
|
||||
:var_arg => nil,
|
||||
:args => [],
|
||||
:name => "tat"
|
||||
}
|
||||
]
|
||||
|
||||
assert_equal(expected, parsed_stuff[:functions])
|
||||
end
|
||||
|
||||
should "extract and return function declarations with attributes" do
|
||||
|
||||
source =
|
||||
"static \tint \n Foo(int a, unsigned int b);\n" +
|
||||
"goodness \t bool bar \n(uint la, int de, bool da);\n"
|
||||
|
||||
@parser = CMockHeaderParser.new(source, /\w+\**/, ['static', 'goodness'])
|
||||
parsed_stuff = @parser.parse
|
||||
|
||||
expected =
|
||||
[
|
||||
{
|
||||
:modifier => "static",
|
||||
:args_string => "int a, unsigned int b",
|
||||
:rettype => "int",
|
||||
:var_arg => nil,
|
||||
:args => [{:type => "int", :name => "a"}, {:type => "unsigned int", :name => "b"}],
|
||||
:name => "Foo"
|
||||
},
|
||||
|
||||
{
|
||||
:modifier => "goodness",
|
||||
:args_string => "uint la, int de, bool da",
|
||||
:rettype => "bool",
|
||||
:var_arg => nil,
|
||||
:args =>
|
||||
[
|
||||
{:type => "uint", :name => "la"},
|
||||
{:type => "int", :name => "de"},
|
||||
{:type => "bool", :name => "da"}
|
||||
],
|
||||
:name => "bar"
|
||||
}
|
||||
]
|
||||
|
||||
assert_equal(expected, parsed_stuff[:functions])
|
||||
end
|
||||
|
||||
should "extract and return function declarations with variable argument lists" do
|
||||
|
||||
source =
|
||||
"\tint \n printf(char * const format, ...);\n" +
|
||||
"bool bar \n(...);\n"
|
||||
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
parsed_stuff = @parser.parse
|
||||
|
||||
expected =
|
||||
[
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "char * const format",
|
||||
:rettype => "int",
|
||||
:var_arg => "...",
|
||||
:args => [{:type => "char * const", :name => "format"}],
|
||||
:name => "printf"
|
||||
},
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "void",
|
||||
:rettype => "bool",
|
||||
:var_arg => "...",
|
||||
:args => [],
|
||||
:name => "bar"
|
||||
}
|
||||
]
|
||||
|
||||
assert_equal(expected, parsed_stuff[:functions])
|
||||
end
|
||||
|
||||
should "attach * to type" do
|
||||
|
||||
source =
|
||||
"MY_STRUCT* HooWah(char * format);\n" +
|
||||
"bool* HotShot(HIS_STRUCT *p, unsigned int * pint);\n"
|
||||
|
||||
@parser = CMockHeaderParser.new(source)
|
||||
parsed_stuff = @parser.parse
|
||||
|
||||
expected =
|
||||
[
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "char * format",
|
||||
:rettype => "MY_STRUCT*",
|
||||
:var_arg => nil,
|
||||
:args => [{:type => "char *", :name => "format"}],
|
||||
:name => "HooWah"
|
||||
},
|
||||
|
||||
{
|
||||
:modifier => "",
|
||||
:args_string => "HIS_STRUCT *p, unsigned int * pint",
|
||||
:rettype => "bool*",
|
||||
:var_arg => nil,
|
||||
:args =>
|
||||
[
|
||||
{:type => "HIS_STRUCT", :name => "*p"},
|
||||
{:type => "unsigned int *", :name => "pint"}
|
||||
],
|
||||
:name => "HotShot"
|
||||
}
|
||||
]
|
||||
|
||||
assert_equal(expected, parsed_stuff[:functions])
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user