class GeneratorTestRunner constructor :configurator, :file_path_utils, :file_wrapper def find_test_cases(test_file) tests = [] tests_and_line_numbers = [] lines = [] # if we don't have preprocessor assistance, do some basic preprocessing of our own if (not @configurator.project_use_test_preprocessor) source = @file_wrapper.read(test_file) # remove line comments source = source.gsub(/\/\/.*$/, '') # remove block comments source = source.gsub(/\/\*.*?\*\//m, '') # treat preprocessor directives as a logical line lines = source.split(/(^\s*\#.*$) | (;|\{|\}) /x) # match ;, {, and } as end of lines # otherwise, read the preprocessed file raw else lines = @file_wrapper.read( @file_path_utils.form_preprocessed_file_filepath(test_file) ).split(/;|\{|\}/) end # step 1. find test functions in (possibly preprocessed) file # (note that lines are not broken up at end of lines) lines.each do |line| if (line =~ /^\s*void\s+((T|t)est.*)\s*\(\s*(void)?\s*\)/m) tests << ($1.strip) end end # step 2. associate test functions with line numbers in (non-preprocessed) original file # (note that this time we must scan file contents broken up by end of lines) raw_lines = @file_wrapper.read(test_file).split("\n") raw_index = 0 tests.each do |test| raw_lines[raw_index..-1].each_with_index do |line, index| # test function might be declared across lines; look for it by its name followed # by a few tell-tale signs if (line =~ /#{test}\s*($|\(|\()/) raw_index += (index + 1) tests_and_line_numbers << {:test => test, :line_number => raw_index} break end end end return tests_and_line_numbers end def create_header(output, mock_list) output << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" output << "#include \"unity.h\"\n" @configurator.test_runner_includes.each do |include| output << "#include \"#{include}\"\n" end output << "#include \n" output << "#include \n" if (@configurator.project_use_exceptions == true) output << "#include \"CException.h\"\n" end unless (mock_list.empty?) header_extension = @configurator.extension_header mock_list.each do |mock| output << "#include \"#{mock}#{header_extension}\"\n" end if (@configurator.cmock_enforce_strict_ordering == true) output << "\n" output << "int GlobalExpectCount;\n" output << "int GlobalVerifyOrder;\n" output << "char* GlobalOrderError;\n" end end output << "\n" output << "char MessageBuffer[50];\n" end def create_externs(output, test_cases) output << "\n" output << "extern void setUp(void);\n" output << "extern void tearDown(void);\n" output << "\n" if not test_cases.empty? test_cases.each do |item| output << "extern void #{item[:test]}(void);\n" end end def create_mock_management(output, mock_list) unless (mock_list.empty?) header_extension = @configurator.extension_header output << "\n" output << "static void CMock_Init(void)\n" output << "{\n" if (@configurator.cmock_enforce_strict_ordering == true) output << " GlobalExpectCount = 0;\n" output << " GlobalVerifyOrder = 0;\n" output << " GlobalOrderError = NULL;\n" end mock_list.each do |mock| output << " #{mock.sub(/#{'\\'+header_extension}/, '')}_Init();\n" end output << "}\n" output << "\n" output << "static void CMock_Verify(void)\n" output << "{\n" mock_list.each do |mock| output << " #{mock.sub(/#{'\\'+header_extension}/, '')}_Verify();\n" end output << "}\n" output << "\n" output << "static void CMock_Destroy(void)\n" output << "{\n" mock_list.each do |mock| output << " #{mock.sub(/#{'\\'+header_extension}/, '')}_Destroy();\n" end output << "}\n" output << "\n" output << "void CMock_VerifyAndReset(void)\n" output << "{\n" output << " CMock_Verify();\n" output << " CMock_Destroy();\n" output << " CMock_Init();\n" output << "}\n" output << "\n" end end def create_runtest(output, mock_list, test_cases) unless(test_cases.empty?) use_exceptions = @configurator.project_use_exceptions tab = ' ' # default spacing tab = ' ' if (use_exceptions) output << "\n" output << "static void TestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum)\n" output << "{\n" output << " Unity.CurrentTestName = FuncName;\n" output << " Unity.CurrentTestLineNumber = FuncLineNum;\n" output << " Unity.NumberOfTests++;\n" output << " if (TEST_PROTECT())\n" output << " {\n" output << " CEXCEPTION_T e;\n" if use_exceptions output << " Try {\n" if use_exceptions output << "#{tab}CMock_Init();\n" unless (mock_list.empty?) output << "#{tab}setUp();\n" output << "#{tab}Func();\n" output << "#{tab}CMock_Verify();\n" unless (mock_list.empty?) output << " } Catch(e) { TEST_FAIL_MESSAGE(\"Unhandled Exception!\"); }\n" if use_exceptions output << " }\n" output << " CMock_Destroy();\n" unless (mock_list.empty?) output << " if (TEST_PROTECT() && !(Unity.CurrentTestIgnored))\n" output << " {\n" output << " tearDown();\n" output << " }\n" output << " UnityConcludeTest();\n" output << "}\n" end end def create_main(output, module_name, test_cases) output << "\n" output << "int main(void)\n" output << "{\n" output << " UnityBegin();\n" output << " Unity.TestFile = \"#{module_name}\";\n" output << "\n" output << " // RUN_TEST calls runTest\n" unless (test_cases.empty?) test_cases.each do |item| output << " RUN_TEST(#{item[:test]}, #{item[:line_number]});\n" end output << "\n" output << " return UnityEnd();\n" output << "}\n" output << "\n" end end