• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ==========================================
2#   Unity Project - A Test Framework for C
3#   Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
4#   [Released under MIT License. Please refer to license.txt for details]
5# ==========================================
6
7require 'fileutils'
8require_relative '../../auto/unity_test_summary'
9require_relative '../../auto/generate_test_runner'
10require_relative '../../auto/colour_reporter'
11require_relative '../../auto/yaml_helper'
12C_EXTENSION = '.c'.freeze
13
14def load_configuration(config_file)
15  $cfg_file = config_file
16  $cfg = YamlHelper.load_file($cfg_file)
17end
18
19def configure_clean
20  CLEAN.include("#{$cfg['compiler']['build_path']}*.*") unless $cfg['compiler']['build_path'].nil?
21end
22
23def configure_toolchain(config_file = DEFAULT_CONFIG_FILE)
24  config_file += '.yml' unless config_file =~ /\.yml$/
25  load_configuration(config_file)
26  configure_clean
27end
28
29def unit_test_files
30  path = "#{$cfg['compiler']['unit_tests_path']}Test*#{C_EXTENSION}"
31  path.tr!('\\', '/')
32  FileList.new(path)
33end
34
35def local_include_dirs
36  include_dirs = $cfg['compiler']['includes']['items'].dup
37  include_dirs.delete_if { |dir| dir.is_a?(Array) }
38  include_dirs
39end
40
41def extract_headers(filename)
42  includes = []
43  lines = File.readlines(filename)
44  lines.each do |line|
45    m = line.match(/^\s*#include\s+"\s*(.+\.[hH])\s*"/)
46    includes << m[1] unless m.nil?
47  end
48  includes
49end
50
51def find_source_file(header, paths)
52  paths.each do |dir|
53    src_file = dir + header.ext(C_EXTENSION)
54    return src_file if File.exist?(src_file)
55  end
56  nil
57end
58
59def tackit(strings)
60  if strings.is_a?(Array)
61    "\"#{strings.join}\""
62  else
63    strings
64  end
65end
66
67def squash(prefix, items)
68  result = ''
69  items.each { |item| result += " #{prefix}#{tackit(item)}" }
70  result
71end
72
73def build_compiler_fields
74  command = tackit($cfg['compiler']['path'])
75  defines = if $cfg['compiler']['defines']['items'].nil?
76              ''
77            else
78              squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'])
79            end
80  options = squash('', $cfg['compiler']['options'])
81  includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items'])
82  includes = includes.gsub(/\\ /, ' ').gsub(/\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)
83
84  { command: command, defines: defines, options: options, includes: includes }
85end
86
87def compile(file, _defines = [])
88  compiler = build_compiler_fields
89  cmd_str  = "#{compiler[:command]}#{compiler[:defines]}#{compiler[:options]}#{compiler[:includes]} #{file} " \
90             "#{$cfg['compiler']['object_files']['prefix']}#{$cfg['compiler']['object_files']['destination']}"
91  obj_file = "#{File.basename(file, C_EXTENSION)}#{$cfg['compiler']['object_files']['extension']}"
92  execute(cmd_str + obj_file)
93  obj_file
94end
95
96def build_linker_fields
97  command = tackit($cfg['linker']['path'])
98  options = if $cfg['linker']['options'].nil?
99              ''
100            else
101              squash('', $cfg['linker']['options'])
102            end
103  includes = if $cfg['linker']['includes'].nil? || $cfg['linker']['includes']['items'].nil?
104               ''
105             else
106               squash($cfg['linker']['includes']['prefix'], $cfg['linker']['includes']['items'])
107             end.gsub(/\\ /, ' ').gsub(/\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)
108
109  { command: command, options: options, includes: includes }
110end
111
112def link_it(exe_name, obj_list)
113  linker = build_linker_fields
114  cmd_str = "#{linker[:command]}#{linker[:options]}#{linker[:includes]}"
115  cmd_str += " #{(obj_list.map { |obj| "#{$cfg['linker']['object_files']['path']}#{obj}" }).join(' ')}"
116  cmd_str += " #{$cfg['linker']['bin_files']['prefix']} "
117  cmd_str += $cfg['linker']['bin_files']['destination']
118  cmd_str += exe_name + $cfg['linker']['bin_files']['extension']
119  execute(cmd_str)
120end
121
122def build_simulator_fields
123  return nil if $cfg['simulator'].nil?
124
125  command = if $cfg['simulator']['path'].nil?
126              ''
127            else
128              "#{tackit($cfg['simulator']['path'])} "
129            end
130  pre_support = if $cfg['simulator']['pre_support'].nil?
131                  ''
132                else
133                  squash('', $cfg['simulator']['pre_support'])
134                end
135  post_support = if $cfg['simulator']['post_support'].nil?
136                   ''
137                 else
138                   squash('', $cfg['simulator']['post_support'])
139                 end
140
141  { command: command, pre_support: pre_support, post_support: post_support }
142end
143
144def execute(command_string, verbose = true, raise_on_fail = true)
145  report command_string
146  output = `#{command_string}`.chomp
147  report(output) if verbose && !output.nil? && !output.empty?
148
149  if !$?.nil? && !$?.exitstatus.zero? && raise_on_fail
150    raise "Command failed. (Returned #{$?.exitstatus})"
151  end
152
153  output
154end
155
156def report_summary
157  summary = UnityTestSummary.new
158  summary.root = __dir__
159  results_glob = "#{$cfg['compiler']['build_path']}*.test*"
160  results_glob.tr!('\\', '/')
161  results = Dir[results_glob]
162  summary.targets = results
163  summary.run
164  fail_out 'FAIL: There were failures' if summary.failures > 0
165end
166
167def run_tests(test_files)
168  report 'Running system tests...'
169
170  # Tack on TEST define for compiling unit tests
171  load_configuration($cfg_file)
172  test_defines = ['TEST']
173  $cfg['compiler']['defines']['items'] = [] if $cfg['compiler']['defines']['items'].nil?
174  $cfg['compiler']['defines']['items'] << 'TEST'
175
176  include_dirs = local_include_dirs
177
178  # Build and execute each unit test
179  test_files.each do |test|
180    obj_list = []
181
182    # Detect dependencies and build required required modules
183    extract_headers(test).each do |header|
184      # Compile corresponding source file if it exists
185      src_file = find_source_file(header, include_dirs)
186      obj_list << compile(src_file, test_defines) unless src_file.nil?
187    end
188
189    # Build the test runner (generate if configured to do so)
190    test_base = File.basename(test, C_EXTENSION)
191    runner_name = "#{test_base}_Runner.c"
192    if $cfg['compiler']['runner_path'].nil?
193      runner_path = $cfg['compiler']['build_path'] + runner_name
194      test_gen = UnityTestRunnerGenerator.new($cfg_file)
195      test_gen.run(test, runner_path)
196    else
197      runner_path = $cfg['compiler']['runner_path'] + runner_name
198    end
199
200    obj_list << compile(runner_path, test_defines)
201
202    # Build the test module
203    obj_list << compile(test, test_defines)
204
205    # Link the test executable
206    link_it(test_base, obj_list)
207
208    # Execute unit test and generate results file
209    simulator = build_simulator_fields
210    executable = $cfg['linker']['bin_files']['destination'] + test_base + $cfg['linker']['bin_files']['extension']
211    cmd_str = if simulator.nil?
212                executable
213              else
214                "#{simulator[:command]} #{simulator[:pre_support]} #{executable} #{simulator[:post_support]}"
215              end
216    output = execute(cmd_str, true, false)
217    test_results = $cfg['compiler']['build_path'] + test_base
218    test_results += if output.match(/OK$/m).nil?
219                      '.testfail'
220                    else
221                      '.testpass'
222                    end
223    File.open(test_results, 'w') { |f| f.print output }
224  end
225end
226
227def build_application(main)
228  report 'Building application...'
229
230  obj_list = []
231  load_configuration($cfg_file)
232  main_path = $cfg['compiler']['source_path'] + main + C_EXTENSION
233
234  # Detect dependencies and build required required modules
235  include_dirs = get_local_include_dirs
236  extract_headers(main_path).each do |header|
237    src_file = find_source_file(header, include_dirs)
238    obj_list << compile(src_file) unless src_file.nil?
239  end
240
241  # Build the main source file
242  main_base = File.basename(main_path, C_EXTENSION)
243  obj_list << compile(main_path)
244
245  # Create the executable
246  link_it(main_base, obj_list)
247end
248
249def fail_out(msg)
250  puts msg
251  puts 'Not returning exit code so continuous integration can pass'
252  #    exit(-1) # Only removed to pass example_3, which has failing tests on purpose.
253  #               Still fail if the build fails for any other reason.
254end
255