class UnityTestRunnerGenerator def run(input_file, output_file, additional_includes=[], tab=' ') @tab = tab tests = [] includes = [] used_mocks = [] module_name = File.basename(input_file) File.open(input_file, 'r') do |input| tests = find_tests(input) includes = find_includes(input) used_mocks = find_mocks(includes) end File.open(output_file, 'w') do |output| create_header(output, used_mocks, additional_includes) create_externs(output, tests, used_mocks) create_mock_management(output, used_mocks) create_runtest(output, used_mocks) create_main(output, module_name, tests) end all_files_used = [input_file, output_file] all_files_used += includes.map {|filename| filename + '.c'} unless includes.empty? all_files_used += additional_includes unless additional_includes.empty? return all_files_used.uniq end def find_tests(input_file) input_file.rewind tests = [] source = input_file.read() 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.each do |line| if line =~ /^\s*void\s+test(.*?)\s*\(\s*void\s*\)/ tests << "test" + $1 end end return tests end def find_includes(input_file) input_file.rewind includes = [] input_file.readlines.each do |line| scan_results = line.scan(/^#include\s+\"\s*(.+)\.h\s*\"/) includes << scan_results[0][0] if (scan_results.size > 0) end return includes end def find_mocks(includes) mock_headers = [] includes.each do |include_file| mock_headers << include_file if include_file.include? "Mock" end return mock_headers end def create_header(output, mocks, additional_includes=[]) output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') output.puts('#include "unity.h"') additional_includes.each do |includes| output.puts("#include \"#{includes}.h\"") end mocks.each do |mock| output.puts("#include \"#{mock}.h\"") end output.puts('#include ') output.puts('#include ') output.puts('') output.puts('jmp_buf AbortFrame;') output.puts('') output.puts('char MessageBuffer[50];') end def create_externs(output, tests, mocks) output.puts('') output.puts("extern void setUp(void);") output.puts("extern void tearDown(void);") output.puts('') tests.each do |test| output.puts("extern void #{test}(void);") end output.puts('') end def create_mock_management(output, mocks) unless (mocks.empty?) output.puts("static void CMock_Init(void)") output.puts("{") mocks.each do |mock| output.puts("#{@tab}#{mock}_Init();") end output.puts("}\n") output.puts("static void CMock_Verify(void)") output.puts("{") mocks.each do |mock| output.puts("#{@tab}#{mock}_Verify();") end output.puts("}\n") output.puts("static void CMock_Destroy(void)") output.puts("{") mocks.each do |mock| output.puts("#{@tab}#{mock}_Destroy();") end output.puts("}\n") end end def create_runtest(output, used_mocks) output.puts("static void runTest(UnityTestFunction test)") output.puts("{") output.puts("#{@tab}if (TEST_PROTECT())") output.puts("#{@tab}{") output.puts("#{@tab}#{@tab}CMock_Init();") unless (used_mocks.empty?) output.puts("#{@tab}#{@tab}setUp();") output.puts("#{@tab}#{@tab}test();") output.puts("#{@tab}#{@tab}CMock_Verify();") unless (used_mocks.empty?) output.puts("#{@tab}}") output.puts("#{@tab}CMock_Destroy();") unless (used_mocks.empty?) output.puts("#{@tab}tearDown();") output.puts("}") end def create_main(output, module_name, tests) output.puts() output.puts() output.puts("int main(void)") output.puts("{") output.puts("#{@tab}Unity.TestFile = \"#{module_name}\";") output.puts("#{@tab}UnityBegin();") output.puts() output.puts("#{@tab}// RUN_TEST calls runTest") tests.each do |test| output.puts("#{@tab}RUN_TEST(#{test});") end output.puts() output.puts("#{@tab}UnityEnd();") output.puts("#{@tab}return 0;") output.puts("}") end end if ($0 == __FILE__) usage = "usage: ruby #{__FILE__} input_test_file output_test_runner" if !ARGV[0] puts usage exit 1 end ARGV[1] = ARGV[0].gsub(".c","_sRunner.c") if (!ARGV[1]) UnityTestRunnerGenerator.new UnityTestRunnerGenerator.run(ARGV[0], ARGV[1]) end