generate_test_runner.rb 5.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

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 <setjmp.h>')
    output.puts('#include <stdio.h>')
    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();")
134
    output.puts("#{@tab}#{@tab}CMock_Verify();") unless (used_mocks.empty?)
135 136 137 138 139 140 141 142 143 144
    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()
145
    output.puts("int main(void)")
146 147 148 149 150 151 152 153 154 155 156 157
    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();")
158
    output.puts("#{@tab}return 0;")
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    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])
  
M
mvandervoord 已提交
175 176 177 178
  includes = []
  includes = ARGV.slice(2..-1) if (ARGV.size > 2)
  
  UnityTestRunnerGenerator.new.run(ARGV[0], ARGV[1], includes)
179
end