From e6c84d9e07637cc2b1aec55f306912aa5b6de298 Mon Sep 17 00:00:00 2001 From: mkarlesky Date: Thu, 14 May 2009 19:10:42 +0000 Subject: [PATCH] completed first pass at testing slimmed down cmock_header_parser using new function prototype parser; added functionality to raise exception upon failed parsing or no prototypes in file to be parsed git-svn-id: http://cmock.svn.sourceforge.net/svnroot/cmock/trunk@95 bf332499-1b4d-0410-844d-d2d48d5cc64c --- lib/cmock.rb | 2 +- lib/cmock_header_parser.rb | 10 +- test/unit/cmock_header_parser_test.rb | 364 +++++++++++++++----------- 3 files changed, 225 insertions(+), 151 deletions(-) diff --git a/lib/cmock.rb b/lib/cmock.rb index fa458e8..b29315f 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -32,7 +32,7 @@ class CMock path = File.dirname(src) @cfg.set_path(path) - cm_parser = CMockHeaderParser.new(CMockFunctionPrototypeParser.new, File.read(src), @cfg) + cm_parser = CMockHeaderParser.new(CMockFunctionPrototypeParser.new, File.read(src), @cfg, name) cm_unityhelper = CMockUnityHelperParser.new(@cfg) cm_writer = CMockFileWriter.new(@cfg) cm_gen_utils = CMockGeneratorUtils.new(@cfg, {:unity_helper => cm_unityhelper}) diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index e5fdb56..1aa65f0 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -3,12 +3,15 @@ class CMockHeaderParser attr_reader :src_lines, :prototypes, :c_attributes - def initialize(parser, source, cfg) + def initialize(parser, source, cfg, name) @src_lines = [] @prototypes = [] - @c_attributes = cfg.attributes @prototype_parse_matcher = /([\d\w\s\*\(\),]+??)\(([\d\w\s\*\(\),\.]*)\)$/m + + @c_attributes = cfg.attributes @parser = parser + @name = name + import_source(source) end @@ -41,6 +44,8 @@ class CMockHeaderParser source.gsub!(/^\s+/, '') # remove extra white space source.gsub!(/\s+$/, '') # remove extra white space + source.gsub!(/\s*\(\s*/, '(') # remove extra white space + source.gsub!(/\s*\)\s*/, ')') # remove extra white space source.gsub!(/\s+/, ' ') # remove extra white space @src_lines = source.split(/\s*;\s*/) # split source at end of statements (removing extra white space) @@ -52,6 +57,7 @@ class CMockHeaderParser @src_lines.each do |line| @prototypes << line if (line =~ @prototype_parse_matcher) end + raise "No function prototypes found in '#{@name}'" if @prototypes.empty? end def parse_prototype(prototype) diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 3248e93..85b3754 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -5,25 +5,28 @@ class CMockHeaderParserTest < Test::Unit::TestCase def setup create_mocks :config, :prototype_parser, :parsed + @test_name = 'test_file.h' @config.expect.attributes.returns(['static', 'inline', '__ramfunc']) end def teardown end + should "create and initialize variables to defaults appropriately" do - @parser = CMockHeaderParser.new(@prototype_parser, "", @config) + @parser = CMockHeaderParser.new(@prototype_parser, "", @config, @test_name) assert_equal([], @parser.prototypes) assert_equal([], @parser.src_lines) assert_equal(['static', 'inline', '__ramfunc'], @parser.c_attributes) end + should "strip out line comments" do source = " abcd;\n" + "// hello;\n" + "who // is you\n" - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [ @@ -34,13 +37,14 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.src_lines) end + should "remove block comments" do source = " abcd;\n" + "/* hello;*/\n" + "who /* is you\n" + "// embedded line comment */\n" - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [ @@ -51,23 +55,25 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.src_lines) end - should "treat preprocessor directives as single line" do + + should "remove preprocessor directives" do source = "#when stuff_happens\n" + "#ifdef _TEST\n" + "#pragma stack_switch" - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [] assert_equal(expected, @parser.src_lines) end + should "smush lines together that contain continuation characters" do source = "hoo hah \\\n" + "when \\ \n" - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [ @@ -77,6 +83,7 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.src_lines) end + should "remove typedef statements" do source = "typedef uint32 (unsigned int)\n" + @@ -84,7 +91,7 @@ class CMockHeaderParserTest < Test::Unit::TestCase "typedef who cares what really comes here \\\n" + # exercise multiline typedef " continuation\n" + "this should remain!" - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [ @@ -94,6 +101,7 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.src_lines) end + should "remove defines" do source = "#define whatever you feel like defining\n" + @@ -102,7 +110,7 @@ class CMockHeaderParserTest < Test::Unit::TestCase "#deFINE\n" + "#define get_foo() \\\n ((Thing)foo.bar)" # exercise multiline define - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [ @@ -112,8 +120,8 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.src_lines) end - should "handle odd case of typedef'd void" do - + + should "handle odd case of typedef'd void" do source = "typedef void SILLY_VOID_TYPE1;\n" + "typedef (void) SILLY_VOID_TYPE2 ;\n" + @@ -122,7 +130,7 @@ class CMockHeaderParserTest < Test::Unit::TestCase "void\n shiz(SILLY_VOID_TYPE1 *);\n" + "void tat(FUNCPTR);\n" - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [ @@ -134,12 +142,12 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.src_lines) end - should "strip default values from function parameter lists" do - + + should "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" - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) expected = [ @@ -149,149 +157,209 @@ class CMockHeaderParserTest < Test::Unit::TestCase assert_equal(expected, @parser.src_lines) end - should "raise upon prototype parsing failure" do - + + should "raise upon empty file" do + source = '' + + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, 'thinger.h') + + # ensure it's expected type of exception + assert_raise RuntimeError do + @parser.parse + end + + assert_equal([], @parser.prototypes) + + # verify exception message + begin + @parser.parse + rescue RuntimeError => e + assert_equal("No function prototypes found in 'thinger.h'", e.message) + end + end + + + should "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)" + + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, 'hello_world.h') + + # ensure it's expected type of exception + assert_raise(RuntimeError) do + @parser.parse + end + + assert_equal([], @parser.prototypes) + + # verify exception message + begin + @parser.parse + rescue RuntimeError => e + assert_equal("No function prototypes found in 'hello_world.h'", e.message) + end + end + + + should "raise upon prototype parsing failure" do source = "int Foo(int a, unsigned int b);\n" + "void bar \n(uint la, int de, bool da) ; \n" @prototype_parser.expect.parse('int Foo(int a, unsigned int b)').returns(nil) + @prototype_parser.expect.parse('int Foo(int a, unsigned int b)').returns(nil) - @parser = CMockHeaderParser.new(@prototype_parser, source, @config) - - begin + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) + + # ensure it's expected type of exception + assert_raise(RuntimeError) do @parser.parse - assert_fail('should have raised') - rescue end + + # verify exception message + begin + @parser.parse + rescue RuntimeError => e + assert_equal("Failed parsing function prototype: 'int Foo(int a, unsigned int b)'", e.message) + end 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 FunkyChicken (\n uint la,\n int de,\n bool da);\n" + - # "void\n shiz(void);\n" + - # "void tat();\n" + - # # following lines should yield no function declarations: - # "#define get_foo() \\\n (Thing)foo())\n" + - # "ARRAY_TYPE array[((U8)10)];\n" + - # "THINGER_MASK = (0x0001 << 5),\n" - # - # @parser = CMockHeaderParser.new(@prototype_parser, source, @config) - # 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 => "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 => "FunkyChicken" - # }, - # - # { - # :modifier => "", - # :args_string => "void", - # :rettype => "void", - # :var_arg => nil, - # :args => [], - # :name => "shiz" - # }, - # - # { - # :modifier => "", - # :args_string => "void", - # :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" + - # "inline\t bool bar \n(uint la, int de, bool da);\n" + - # "inline static __ramfunc bool bar ( uint thinger );\n" - # - # @parser = CMockHeaderParser.new(@prototype_parser, source, @config) - # 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 => "inline", - # :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" - # }, - # - # { - # :modifier => "inline static __ramfunc", - # :args_string => "uint thinger", - # :rettype => "bool", - # :var_arg => nil, - # :args => - # [ - # {:type => "uint", :name => "thinger"}, - # ], - # :name => "bar" - # } - # ] - # - # assert_equal(expected, parsed_stuff[:functions]) - # end + + should "extract and return function declarations" do + source = + "int Foo(int a, unsigned int b);\n" + + "void FunkyChicken (\n uint la,\n int de,\n bool da) ; \n" + + " void \n tat();\n" + + # following lines should yield no function prototypes: + "#define get_foo() \\\n (Thing)foo())\n" + + "ARRAY_TYPE array[((U8)10)];\n" + + "THINGER_MASK = (0x0001 << 5),\n" + + @prototype_parser.expect.parse('int Foo(int a, unsigned int b)').returns(@parsed) + + @parsed.expect.get_function_name.returns('buzz lightyear') + @parsed.expect.get_argument_list.returns('woody') + @parsed.expect.get_arguments.returns([{:type => 'what up', :name => 'dawg'}]) + @parsed.expect.get_return_type.returns('bo peep') + @parsed.expect.get_var_arg.returns('...') + + @prototype_parser.expect.parse('void FunkyChicken(uint la, int de, bool da)').returns(@parsed) + + @parsed.expect.get_function_name.returns('marty') + @parsed.expect.get_argument_list.returns('mcfly') + @parsed.expect.get_arguments.returns([{:type => 'back', :name => 'to'}]) + @parsed.expect.get_return_type.returns('the future') + @parsed.expect.get_var_arg.returns(nil) + + @prototype_parser.expect.parse('void tat()').returns(@parsed) + + @parsed.expect.get_function_name.returns('neo') + @parsed.expect.get_argument_list.returns('the matrix') + @parsed.expect.get_arguments.returns([{:type => 'trinity', :name => 'the one'}]) + @parsed.expect.get_return_type.returns('agent smith') + @parsed.expect.get_var_arg.returns('...') + + expected_prototypes = + [ + 'int Foo(int a, unsigned int b)', + 'void FunkyChicken(uint la, int de, bool da)', + 'void tat()' + ] + + expected_hashes = + [ + { + :modifier => '', + :args_string => 'woody', + :rettype => 'bo peep', + :var_arg => '...', + :args => [{:type => 'what up', :name => 'dawg'}], + :name => 'buzz lightyear' + }, + + { + :modifier => '', + :args_string => 'mcfly', + :rettype => 'the future', + :var_arg => nil, + :args => [{:type => 'back', :name => 'to'}], + :name => 'marty' + }, + + { + :modifier => '', + :args_string => 'the matrix', + :rettype => 'agent smith', + :var_arg => '...', + :args => [{:type => 'trinity', :name => 'the one'}], + :name => 'neo' + }, + ] + + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) + parsed_stuff = @parser.parse + + assert_equal(expected_prototypes, @parser.prototypes) + assert_equal(expected_hashes, parsed_stuff[:functions]) + end + + + should "extract and return function declarations with attributes" do + source = + "static inline int Foo(int a, unsigned int b);\n" + + " __ramfunc void \n tat();\n" + + @prototype_parser.expect.parse('int Foo(int a, unsigned int b)').returns(@parsed) + + @parsed.expect.get_function_name.returns('buzz lightyear') + @parsed.expect.get_argument_list.returns('woody') + @parsed.expect.get_arguments.returns([{:type => 'what up', :name => 'dawg'}]) + @parsed.expect.get_return_type.returns('bo peep') + @parsed.expect.get_var_arg.returns('...') + + @prototype_parser.expect.parse('void tat()').returns(@parsed) + + @parsed.expect.get_function_name.returns('neo') + @parsed.expect.get_argument_list.returns('the matrix') + @parsed.expect.get_arguments.returns([{:type => 'trinity', :name => 'the one'}]) + @parsed.expect.get_return_type.returns('agent smith') + @parsed.expect.get_var_arg.returns('...') + + expected_prototypes = + [ + 'int Foo(int a, unsigned int b)', + 'void tat()' + ] + + expected_hashes = + [ + { + :modifier => 'static inline', + :args_string => 'woody', + :rettype => 'bo peep', + :var_arg => '...', + :args => [{:type => 'what up', :name => 'dawg'}], + :name => 'buzz lightyear' + }, + + { + :modifier => '__ramfunc', + :args_string => 'the matrix', + :rettype => 'agent smith', + :var_arg => '...', + :args => [{:type => 'trinity', :name => 'the one'}], + :name => 'neo' + }, + ] + + @parser = CMockHeaderParser.new(@prototype_parser, source, @config, @test_name) + parsed_stuff = @parser.parse + + assert_equal(expected_prototypes, @parser.prototypes) + assert_equal(expected_hashes, parsed_stuff[:functions]) + end end \ No newline at end of file