rakefile_helper.rb 10.1 KB
Newer Older
M
Mark VanderVoord 已提交
1 2 3 4 5 6 7 8
# ==========================================
#   Unity Project - A Test Framework for C
#   Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
#   [Released under MIT License. Please refer to license.txt for details]
# ==========================================

require 'yaml'
require 'fileutils'
J
John Lindgren 已提交
9 10 11
require_relative '../auto/unity_test_summary'
require_relative '../auto/generate_test_runner'
require_relative '../auto/colour_reporter'
M
Mark VanderVoord 已提交
12 13

module RakefileHelpers
14
  C_EXTENSION = '.c'.freeze
M
Mark VanderVoord 已提交
15
  def load_configuration(config_file)
16 17 18 19 20 21
    return if $configured

    $cfg_file = "targets/#{config_file}" unless config_file =~ /[\\|\/]/
    $cfg = YAML.load(File.read($cfg_file))
    $colour_output = false unless $cfg['colour']
    $configured = true if config_file != DEFAULT_CONFIG_FILE
M
Mark VanderVoord 已提交
22 23 24
  end

  def configure_clean
25
    CLEAN.include('build/*.*')
M
Mark VanderVoord 已提交
26 27
  end

28
  def configure_toolchain(config_file = DEFAULT_CONFIG_FILE)
M
Mark VanderVoord 已提交
29 30 31 32 33 34
    config_file += '.yml' unless config_file =~ /\.yml$/
    config_file = config_file unless config_file =~ /[\\|\/]/
    load_configuration(config_file)
    configure_clean
  end

35
  def unit_test_files
36
    path = 'tests/test*' + C_EXTENSION
37
    path.tr!('\\', '/')
M
Mark VanderVoord 已提交
38 39 40
    FileList.new(path)
  end

41
  def local_include_dirs
42 43 44 45
    include_dirs = $cfg[:paths][:includes] || []
    include_dirs += $cfg[:paths][:source] || []
    include_dirs += $cfg[:paths][:test] || []
    include_dirs += $cfg[:paths][:support] || []
46 47
    include_dirs.delete_if { |dir| dir.is_a?(Array) }
    include_dirs
M
Mark VanderVoord 已提交
48 49 50 51 52 53 54
  end

  def extract_headers(filename)
    includes = []
    lines = File.readlines(filename)
    lines.each do |line|
      m = line.match(/^\s*#include\s+\"\s*(.+\.[hH])\s*\"/)
55
      includes << m[1] unless m.nil?
M
Mark VanderVoord 已提交
56
    end
57
    includes
M
Mark VanderVoord 已提交
58 59 60 61 62
  end

  def find_source_file(header, paths)
    paths.each do |dir|
      src_file = dir + header.ext(C_EXTENSION)
63
      return src_file if File.exist?(src_file)
M
Mark VanderVoord 已提交
64
    end
65
    nil
M
Mark VanderVoord 已提交
66 67 68
  end

  def tackit(strings)
69 70 71 72 73 74
    result = if strings.is_a?(Array)
               "\"#{strings.join}\""
             else
               strings
             end
    result
M
Mark VanderVoord 已提交
75 76 77 78 79
  end

  def squash(prefix, items)
    result = ''
    items.each { |item| result += " #{prefix}#{tackit(item)}" }
80
    result
M
Mark VanderVoord 已提交
81 82 83 84
  end

  def should(behave, &block)
    if block
85
      puts 'Should ' + behave
M
Mark VanderVoord 已提交
86 87 88 89 90 91
      yield block
    else
      puts "UNIMPLEMENTED CASE: Should #{behave}"
    end
  end

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
  def build_command_string(hash, values, defines = nil)

    # Replace named and numbered slots
    args = []
    hash[:arguments].each do |arg|
      if arg.include? '$'
        if arg.include? ': COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'
          pattern = arg.gsub(': COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE','')
          [ File.join('..','src') ].each do |f|
            args << pattern.gsub(/\$/,f)
          end

        elsif arg.include? ': COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'
          pattern = arg.gsub(': COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR','')
          [ $extra_paths, 'src', File.join('tests'), File.join('testdata'), $cfg[:paths][:support] ].flatten.uniq.compact.each do |f|
            args << pattern.gsub(/\$/,f)
          end

        elsif arg.include? ': COLLECTION_DEFINES_TEST_AND_VENDOR'
          pattern = arg.gsub(': COLLECTION_DEFINES_TEST_AND_VENDOR','')
          [ $cfg[:defines][:test], defines ].flatten.uniq.compact.each do |f|
            args << pattern.gsub(/\$/,f)
          end

        elsif arg =~ /\$\{(\d+)\}/
          i = $1.to_i - 1
          if (values[i].is_a?(Array))
            values[i].each {|v| args << arg.gsub(/\$\{\d+\}/, v)}
          else
            args << arg.gsub(/\$\{(\d)+\}/, values[i] || '')
          end

        else
          args << arg
M
Mark VanderVoord 已提交
126

127 128 129 130 131
        end
      else
        args << arg
      end
    end
132

133 134
    # Build Command
    return tackit(hash[:executable]) + squash('', args)
M
Mark VanderVoord 已提交
135 136
  end

137 138 139 140 141
  def compile(file, defines = [])
    out_file = File.join('build', File.basename(file, C_EXTENSION)) + $cfg[:extension][:object]
    cmd_str = build_command_string( $cfg[:tools][:test_compiler], [ file, out_file ], defines )
    execute(cmd_str)
    out_file
M
Mark VanderVoord 已提交
142 143 144
  end

  def link_it(exe_name, obj_list)
145 146
    exe_name = File.join('build', File.basename(exe_name))
    cmd_str = build_command_string( $cfg[:tools][:test_linker], [ obj_list, exe_name ] )
M
Mark VanderVoord 已提交
147 148 149
    execute(cmd_str)
  end

150 151 152 153 154 155 156 157 158
  def runtest(bin_name, ok_to_fail = false, extra_args = nil)
    bin_name = File.join('build', File.basename(bin_name))
    extra_args = extra_args.nil? ? "" : " " + extra_args
    if $cfg[:tools][:test_fixture]
      cmd_str = build_command_string( $cfg[:tools][:test_fixture], [ bin_name, extra_args ] )
    else
      cmd_str = bin_name + extra_args
    end
    execute(cmd_str, ok_to_fail)
159 160
  end

161 162 163 164 165 166 167 168 169 170 171 172
  def run_astyle(style_what)
    report "Styling C Code..."
    command = "AStyle " \
              "--style=allman --indent=spaces=4 --indent-switches --indent-preproc-define --indent-preproc-block " \
              "--pad-oper --pad-comma --unpad-paren --pad-header " \
              "--align-pointer=type --align-reference=name " \
              "--add-brackets --mode=c --suffix=none " \
              "#{style_what}"
    execute(command, false)
    report "Styling C:PASS"
  end

173
  def execute(command_string, ok_to_fail = false)
174
    report command_string if $verbose
M
Mark VanderVoord 已提交
175
    output = `#{command_string}`.chomp
176
    report(output) if $verbose && !output.nil? && !output.empty?
177
    raise "Command failed. (Returned #{$?.exitstatus})" if !$?.nil? && !$?.exitstatus.zero? && !ok_to_fail
178
    output
M
Mark VanderVoord 已提交
179 180 181 182
  end

  def report_summary
    summary = UnityTestSummary.new
J
John Lindgren 已提交
183
    summary.root = __dir__
184
    results_glob = File.join('build','*.test*')
185
    results_glob.tr!('\\', '/')
M
Mark VanderVoord 已提交
186
    results = Dir[results_glob]
187
    summary.targets = results
M
Mark VanderVoord 已提交
188 189 190
    report summary.run
  end

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
  def save_test_results(test_base, output)
    test_results = File.join('build',test_base)
    if output.match(/OK$/m).nil?
      test_results += '.testfail'
    else
      report output unless $verbose # Verbose already prints this line, as does a failure
      test_results += '.testpass'
    end
    File.open(test_results, 'w') { |f| f.print output }
  end

  def test_fixtures()
    report "\nRunning Fixture Addon"

    # Get a list of all source files needed
    src_files  = Dir[File.join('..','extras','fixture','src','*.c')]
    src_files += Dir[File.join('..','extras','fixture','test','*.c')]
    src_files += Dir[File.join('..','extras','fixture','test','main','*.c')]
    src_files += Dir[File.join('..','extras','memory','src','*.c')]
    src_files << File.join('..','src','unity.c')

    # Build object files
    $extra_paths = [File.join('..','extras','fixture','src'), File.join('..','extras','memory','src')]
    obj_list = src_files.map { |f| compile(f, ['UNITY_SKIP_DEFAULT_RUNNER', 'UNITY_FIXTURE_NO_EXTRAS']) }

    # Link the test executable
    test_base = File.basename('framework_test', C_EXTENSION)
    link_it(test_base, obj_list)

    # Run and collect output
    output = runtest(test_base + " -v -r")
    save_test_results(test_base, output)
  end
M
Mark VanderVoord 已提交
224

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
  def test_memory()
    { 'w_malloc' => [],
      'wo_malloc' => ['UNITY_EXCLUDE_STDLIB_MALLOC']
    }.each_pair do |name, defs|
      report "\nRunning Memory Addon #{name}"

      # Get a list of all source files needed
      src_files  = Dir[File.join('..','extras','memory','src','*.c')]
      src_files += Dir[File.join('..','extras','memory','test','*.c')]
      src_files += Dir[File.join('..','extras','memory','test','main','*.c')]
      src_files << File.join('..','src','unity.c')

      # Build object files
      $extra_paths = [File.join('..','extras','memory','src')]
      obj_list = src_files.map { |f| compile(f, defs) }

      # Link the test executable
      test_base = File.basename("memory_test_#{name}", C_EXTENSION)
      link_it(test_base, obj_list)

      # Run and collect output
      output = runtest(test_base)
      save_test_results(test_base, output)
    end
  end

  def run_tests(test_files)
    report "\nRunning Unity system tests"
M
Mark VanderVoord 已提交
253

254
    include_dirs = local_include_dirs
M
Mark VanderVoord 已提交
255 256 257

    # Build and execute each unit test
    test_files.each do |test|
258 259 260 261 262 263 264 265 266

      # Drop Out if we're skipping this type of test
      if $cfg[:skip_tests]
        if $cfg[:skip_tests].include?(:parameterized) && test.match(/parameterized/)
          report("Skipping Parameterized Tests for this Target:IGNORE")
          next
        end
      end

267
      report "\nRunning Tests in #{test}"
M
Mark VanderVoord 已提交
268
      obj_list = []
269
      test_defines = []
M
Mark VanderVoord 已提交
270 271 272 273 274

      # Detect dependencies and build required modules
      extract_headers(test).each do |header|
        # Compile corresponding source file if it exists
        src_file = find_source_file(header, include_dirs)
275 276

        obj_list << compile(src_file, test_defines) unless src_file.nil?
M
Mark VanderVoord 已提交
277 278 279 280 281
      end

      # Build the test runner (generate if configured to do so)
      test_base = File.basename(test, C_EXTENSION)
      runner_name = test_base + '_Runner.c'
282
      runner_path = File.join('build',runner_name)
M
Mark VanderVoord 已提交
283 284

      options = $cfg[:unity]
285
      options[:use_param_tests] = test =~ /parameterized/ ? true : false
M
Mark VanderVoord 已提交
286 287 288 289 290 291 292 293 294 295
      UnityTestRunnerGenerator.new(options).run(test, runner_path)
      obj_list << compile(runner_path, test_defines)

      # Build the test module
      obj_list << compile(test, test_defines)

      # Link the test executable
      link_it(test_base, obj_list)

      # Execute unit test and generate results file
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
      output = runtest(test_base)
      save_test_results(test_base, output)
    end
  end

  def run_make_tests()
    [ "make -s",                               # test with all defaults
      #"make -s DEBUG=-m32",                    # test 32-bit architecture with 64-bit support
      #"make -s DEBUG=-m32 UNITY_SUPPORT_64=",  # test 32-bit build without 64-bit types
      "make -s UNITY_INCLUDE_DOUBLE= ",        # test without double
      "cd #{File.join("..","extras","fixture",'test')} && make -s default noStdlibMalloc",
      "cd #{File.join("..","extras","fixture",'test')} && make -s C89",
      "cd #{File.join("..","extras","memory",'test')} && make -s default noStdlibMalloc",
      "cd #{File.join("..","extras","memory",'test')} && make -s C89",
    ].each do |cmd|
      report "Testing '#{cmd}'"
      execute(cmd, false)
M
Mark VanderVoord 已提交
313 314 315
    end
  end
end