135375f98Sopenharmony_ci#!/usr/bin/ruby 235375f98Sopenharmony_ci 335375f98Sopenharmony_ci# ========================================== 435375f98Sopenharmony_ci# Unity Project - A Test Framework for C 535375f98Sopenharmony_ci# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams 635375f98Sopenharmony_ci# [Released under MIT License. Please refer to license.txt for details] 735375f98Sopenharmony_ci# ========================================== 835375f98Sopenharmony_ci 935375f98Sopenharmony_ciclass UnityTestRunnerGenerator 1035375f98Sopenharmony_ci def initialize(options = nil) 1135375f98Sopenharmony_ci @options = UnityTestRunnerGenerator.default_options 1235375f98Sopenharmony_ci case options 1335375f98Sopenharmony_ci when NilClass 1435375f98Sopenharmony_ci @options 1535375f98Sopenharmony_ci when String 1635375f98Sopenharmony_ci @options.merge!(UnityTestRunnerGenerator.grab_config(options)) 1735375f98Sopenharmony_ci when Hash 1835375f98Sopenharmony_ci # Check if some of these have been specified 1935375f98Sopenharmony_ci @options[:has_setup] = !options[:setup_name].nil? 2035375f98Sopenharmony_ci @options[:has_teardown] = !options[:teardown_name].nil? 2135375f98Sopenharmony_ci @options[:has_suite_setup] = !options[:suite_setup].nil? 2235375f98Sopenharmony_ci @options[:has_suite_teardown] = !options[:suite_teardown].nil? 2335375f98Sopenharmony_ci @options.merge!(options) 2435375f98Sopenharmony_ci else 2535375f98Sopenharmony_ci raise 'If you specify arguments, it should be a filename or a hash of options' 2635375f98Sopenharmony_ci end 2735375f98Sopenharmony_ci require_relative 'type_sanitizer' 2835375f98Sopenharmony_ci end 2935375f98Sopenharmony_ci 3035375f98Sopenharmony_ci def self.default_options 3135375f98Sopenharmony_ci { 3235375f98Sopenharmony_ci includes: [], 3335375f98Sopenharmony_ci defines: [], 3435375f98Sopenharmony_ci plugins: [], 3535375f98Sopenharmony_ci framework: :unity, 3635375f98Sopenharmony_ci test_prefix: 'test|spec|should', 3735375f98Sopenharmony_ci mock_prefix: 'Mock', 3835375f98Sopenharmony_ci mock_suffix: '', 3935375f98Sopenharmony_ci setup_name: 'setUp', 4035375f98Sopenharmony_ci teardown_name: 'tearDown', 4135375f98Sopenharmony_ci test_reset_name: 'resetTest', 4235375f98Sopenharmony_ci test_verify_name: 'verifyTest', 4335375f98Sopenharmony_ci main_name: 'main', # set to :auto to automatically generate each time 4435375f98Sopenharmony_ci main_export_decl: '', 4535375f98Sopenharmony_ci cmdline_args: false, 4635375f98Sopenharmony_ci omit_begin_end: false, 4735375f98Sopenharmony_ci use_param_tests: false, 4835375f98Sopenharmony_ci use_system_files: true, 4935375f98Sopenharmony_ci include_extensions: '(?:hpp|hh|H|h)', 5035375f98Sopenharmony_ci source_extensions: '(?:cpp|cc|ino|C|c)' 5135375f98Sopenharmony_ci } 5235375f98Sopenharmony_ci end 5335375f98Sopenharmony_ci 5435375f98Sopenharmony_ci def self.grab_config(config_file) 5535375f98Sopenharmony_ci options = default_options 5635375f98Sopenharmony_ci unless config_file.nil? || config_file.empty? 5735375f98Sopenharmony_ci require_relative 'yaml_helper' 5835375f98Sopenharmony_ci yaml_guts = YamlHelper.load_file(config_file) 5935375f98Sopenharmony_ci options.merge!(yaml_guts[:unity] || yaml_guts[:cmock]) 6035375f98Sopenharmony_ci raise "No :unity or :cmock section found in #{config_file}" unless options 6135375f98Sopenharmony_ci end 6235375f98Sopenharmony_ci options 6335375f98Sopenharmony_ci end 6435375f98Sopenharmony_ci 6535375f98Sopenharmony_ci def run(input_file, output_file, options = nil) 6635375f98Sopenharmony_ci @options.merge!(options) unless options.nil? 6735375f98Sopenharmony_ci 6835375f98Sopenharmony_ci # pull required data from source file 6935375f98Sopenharmony_ci source = File.read(input_file) 7035375f98Sopenharmony_ci source = source.force_encoding('ISO-8859-1').encode('utf-8', replace: nil) 7135375f98Sopenharmony_ci tests = find_tests(source) 7235375f98Sopenharmony_ci headers = find_includes(source) 7335375f98Sopenharmony_ci testfile_includes = @options[:use_system_files] ? (headers[:local] + headers[:system]) : (headers[:local]) 7435375f98Sopenharmony_ci used_mocks = find_mocks(testfile_includes) 7535375f98Sopenharmony_ci testfile_includes = (testfile_includes - used_mocks) 7635375f98Sopenharmony_ci testfile_includes.delete_if { |inc| inc =~ /(unity|cmock)/ } 7735375f98Sopenharmony_ci find_setup_and_teardown(source) 7835375f98Sopenharmony_ci 7935375f98Sopenharmony_ci # build runner file 8035375f98Sopenharmony_ci generate(input_file, output_file, tests, used_mocks, testfile_includes) 8135375f98Sopenharmony_ci 8235375f98Sopenharmony_ci # determine which files were used to return them 8335375f98Sopenharmony_ci all_files_used = [input_file, output_file] 8435375f98Sopenharmony_ci all_files_used += testfile_includes.map { |filename| "#{filename}.c" } unless testfile_includes.empty? 8535375f98Sopenharmony_ci all_files_used += @options[:includes] unless @options[:includes].empty? 8635375f98Sopenharmony_ci all_files_used += headers[:linkonly] unless headers[:linkonly].empty? 8735375f98Sopenharmony_ci all_files_used.uniq 8835375f98Sopenharmony_ci end 8935375f98Sopenharmony_ci 9035375f98Sopenharmony_ci def generate(input_file, output_file, tests, used_mocks, testfile_includes) 9135375f98Sopenharmony_ci File.open(output_file, 'w') do |output| 9235375f98Sopenharmony_ci create_header(output, used_mocks, testfile_includes) 9335375f98Sopenharmony_ci create_externs(output, tests, used_mocks) 9435375f98Sopenharmony_ci create_mock_management(output, used_mocks) 9535375f98Sopenharmony_ci create_setup(output) 9635375f98Sopenharmony_ci create_teardown(output) 9735375f98Sopenharmony_ci create_suite_setup(output) 9835375f98Sopenharmony_ci create_suite_teardown(output) 9935375f98Sopenharmony_ci create_reset(output) 10035375f98Sopenharmony_ci create_run_test(output) unless tests.empty? 10135375f98Sopenharmony_ci create_args_wrappers(output, tests) 10235375f98Sopenharmony_ci create_main(output, input_file, tests, used_mocks) 10335375f98Sopenharmony_ci end 10435375f98Sopenharmony_ci 10535375f98Sopenharmony_ci return unless @options[:header_file] && !@options[:header_file].empty? 10635375f98Sopenharmony_ci 10735375f98Sopenharmony_ci File.open(@options[:header_file], 'w') do |output| 10835375f98Sopenharmony_ci create_h_file(output, @options[:header_file], tests, testfile_includes, used_mocks) 10935375f98Sopenharmony_ci end 11035375f98Sopenharmony_ci end 11135375f98Sopenharmony_ci 11235375f98Sopenharmony_ci def find_tests(source) 11335375f98Sopenharmony_ci tests_and_line_numbers = [] 11435375f98Sopenharmony_ci 11535375f98Sopenharmony_ci # contains characters which will be substituted from within strings, doing 11635375f98Sopenharmony_ci # this prevents these characters from interfering with scrubbers 11735375f98Sopenharmony_ci # @ is not a valid C character, so there should be no clashes with files genuinely containing these markers 11835375f98Sopenharmony_ci substring_subs = { '{' => '@co@', '}' => '@cc@', ';' => '@ss@', '/' => '@fs@' } 11935375f98Sopenharmony_ci substring_re = Regexp.union(substring_subs.keys) 12035375f98Sopenharmony_ci substring_unsubs = substring_subs.invert # the inverse map will be used to fix the strings afterwords 12135375f98Sopenharmony_ci substring_unsubs['@quote@'] = '\\"' 12235375f98Sopenharmony_ci substring_unsubs['@apos@'] = '\\\'' 12335375f98Sopenharmony_ci substring_unre = Regexp.union(substring_unsubs.keys) 12435375f98Sopenharmony_ci source_scrubbed = source.clone 12535375f98Sopenharmony_ci source_scrubbed = source_scrubbed.gsub(/\\"/, '@quote@') # hide escaped quotes to allow capture of the full string/char 12635375f98Sopenharmony_ci source_scrubbed = source_scrubbed.gsub(/\\'/, '@apos@') # hide escaped apostrophes to allow capture of the full string/char 12735375f98Sopenharmony_ci source_scrubbed = source_scrubbed.gsub(/("[^"\n]*")|('[^'\n]*')/) { |s| s.gsub(substring_re, substring_subs) } # temporarily hide problematic characters within strings 12835375f98Sopenharmony_ci source_scrubbed = source_scrubbed.gsub(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks 12935375f98Sopenharmony_ci source_scrubbed = source_scrubbed.gsub(/\/\*.*?\*\//m, '') # remove block comments 13035375f98Sopenharmony_ci source_scrubbed = source_scrubbed.gsub(/\/\/.*$/, '') # remove line comments (all that remain) 13135375f98Sopenharmony_ci lines = source_scrubbed.split(/(^\s*\#.*$) | (;|\{|\}) /x) # Treat preprocessor directives as a logical line. Match ;, {, and } as end of lines 13235375f98Sopenharmony_ci .map { |line| line.gsub(substring_unre, substring_unsubs) } # unhide the problematic characters previously removed 13335375f98Sopenharmony_ci 13435375f98Sopenharmony_ci lines.each_with_index do |line, _index| 13535375f98Sopenharmony_ci # find tests 13635375f98Sopenharmony_ci next unless line =~ /^((?:\s*(?:TEST_(?:CASE|RANGE|MATRIX))\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m 13735375f98Sopenharmony_ci next unless line =~ /^((?:\s*(?:TEST_(?:CASE|RANGE|MATRIX))\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]})\w*)\s*\(\s*(.*)\s*\)/m 13835375f98Sopenharmony_ci 13935375f98Sopenharmony_ci arguments = Regexp.last_match(1) 14035375f98Sopenharmony_ci name = Regexp.last_match(2) 14135375f98Sopenharmony_ci call = Regexp.last_match(3) 14235375f98Sopenharmony_ci params = Regexp.last_match(4) 14335375f98Sopenharmony_ci args = nil 14435375f98Sopenharmony_ci 14535375f98Sopenharmony_ci if @options[:use_param_tests] && !arguments.empty? 14635375f98Sopenharmony_ci args = [] 14735375f98Sopenharmony_ci type_and_args = arguments.split(/TEST_(CASE|RANGE|MATRIX)/) 14835375f98Sopenharmony_ci (1...type_and_args.length).step(2).each do |i| 14935375f98Sopenharmony_ci case type_and_args[i] 15035375f98Sopenharmony_ci when 'CASE' 15135375f98Sopenharmony_ci args << type_and_args[i + 1].sub(/^\s*\(\s*(.*?)\s*\)\s*$/m, '\1') 15235375f98Sopenharmony_ci 15335375f98Sopenharmony_ci when 'RANGE' 15435375f98Sopenharmony_ci args += type_and_args[i + 1].scan(/(\[|<)\s*(-?\d+.?\d*)\s*,\s*(-?\d+.?\d*)\s*,\s*(-?\d+.?\d*)\s*(\]|>)/m).map do |arg_values_str| 15535375f98Sopenharmony_ci exclude_end = arg_values_str[0] == '<' && arg_values_str[-1] == '>' 15635375f98Sopenharmony_ci arg_values_str[1...-1].map do |arg_value_str| 15735375f98Sopenharmony_ci arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i 15835375f98Sopenharmony_ci end.push(exclude_end) 15935375f98Sopenharmony_ci end.map do |arg_values| 16035375f98Sopenharmony_ci Range.new(arg_values[0], arg_values[1], arg_values[3]).step(arg_values[2]).to_a 16135375f98Sopenharmony_ci end.reduce(nil) do |result, arg_range_expanded| 16235375f98Sopenharmony_ci result.nil? ? arg_range_expanded.map { |a| [a] } : result.product(arg_range_expanded) 16335375f98Sopenharmony_ci end.map do |arg_combinations| 16435375f98Sopenharmony_ci arg_combinations.flatten.join(', ') 16535375f98Sopenharmony_ci end 16635375f98Sopenharmony_ci 16735375f98Sopenharmony_ci when 'MATRIX' 16835375f98Sopenharmony_ci single_arg_regex_string = /(?:(?:"(?:\\"|[^\\])*?")+|(?:'\\?.')+|(?:[^\s\]\["',]|\[[\d\S_-]+\])+)/.source 16935375f98Sopenharmony_ci args_regex = /\[((?:\s*#{single_arg_regex_string}\s*,?)*(?:\s*#{single_arg_regex_string})?\s*)\]/m 17035375f98Sopenharmony_ci arg_elements_regex = /\s*(#{single_arg_regex_string})\s*,\s*/m 17135375f98Sopenharmony_ci 17235375f98Sopenharmony_ci args += type_and_args[i + 1].scan(args_regex).flatten.map do |arg_values_str| 17335375f98Sopenharmony_ci ("#{arg_values_str},").scan(arg_elements_regex) 17435375f98Sopenharmony_ci end.reduce do |result, arg_range_expanded| 17535375f98Sopenharmony_ci result.product(arg_range_expanded) 17635375f98Sopenharmony_ci end.map do |arg_combinations| 17735375f98Sopenharmony_ci arg_combinations.flatten.join(', ') 17835375f98Sopenharmony_ci end 17935375f98Sopenharmony_ci end 18035375f98Sopenharmony_ci end 18135375f98Sopenharmony_ci end 18235375f98Sopenharmony_ci 18335375f98Sopenharmony_ci tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 } 18435375f98Sopenharmony_ci end 18535375f98Sopenharmony_ci 18635375f98Sopenharmony_ci tests_and_line_numbers.uniq! { |v| v[:test] } 18735375f98Sopenharmony_ci 18835375f98Sopenharmony_ci # determine line numbers and create tests to run 18935375f98Sopenharmony_ci source_lines = source.split("\n") 19035375f98Sopenharmony_ci source_index = 0 19135375f98Sopenharmony_ci tests_and_line_numbers.size.times do |i| 19235375f98Sopenharmony_ci source_lines[source_index..].each_with_index do |line, index| 19335375f98Sopenharmony_ci next unless line =~ /\s+#{tests_and_line_numbers[i][:test]}(?:\s|\()/ 19435375f98Sopenharmony_ci 19535375f98Sopenharmony_ci source_index += index 19635375f98Sopenharmony_ci tests_and_line_numbers[i][:line_number] = source_index + 1 19735375f98Sopenharmony_ci break 19835375f98Sopenharmony_ci end 19935375f98Sopenharmony_ci end 20035375f98Sopenharmony_ci 20135375f98Sopenharmony_ci tests_and_line_numbers 20235375f98Sopenharmony_ci end 20335375f98Sopenharmony_ci 20435375f98Sopenharmony_ci def find_includes(source) 20535375f98Sopenharmony_ci # remove comments (block and line, in three steps to ensure correct precedence) 20635375f98Sopenharmony_ci source.gsub!(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks 20735375f98Sopenharmony_ci source.gsub!(/\/\*.*?\*\//m, '') # remove block comments 20835375f98Sopenharmony_ci source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain) 20935375f98Sopenharmony_ci 21035375f98Sopenharmony_ci # parse out includes 21135375f98Sopenharmony_ci { 21235375f98Sopenharmony_ci local: source.scan(/^\s*#include\s+"\s*(.+\.#{@options[:include_extensions]})\s*"/).flatten, 21335375f98Sopenharmony_ci system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }, 21435375f98Sopenharmony_ci linkonly: source.scan(/^TEST_SOURCE_FILE\(\s*"\s*(.+\.#{@options[:source_extensions]})\s*"/).flatten 21535375f98Sopenharmony_ci } 21635375f98Sopenharmony_ci end 21735375f98Sopenharmony_ci 21835375f98Sopenharmony_ci def find_mocks(includes) 21935375f98Sopenharmony_ci mock_headers = [] 22035375f98Sopenharmony_ci includes.each do |include_path| 22135375f98Sopenharmony_ci include_file = File.basename(include_path) 22235375f98Sopenharmony_ci mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}\.h$/i 22335375f98Sopenharmony_ci end 22435375f98Sopenharmony_ci mock_headers 22535375f98Sopenharmony_ci end 22635375f98Sopenharmony_ci 22735375f98Sopenharmony_ci def find_setup_and_teardown(source) 22835375f98Sopenharmony_ci @options[:has_setup] = source =~ /void\s+#{@options[:setup_name]}\s*\(/ 22935375f98Sopenharmony_ci @options[:has_teardown] = source =~ /void\s+#{@options[:teardown_name]}\s*\(/ 23035375f98Sopenharmony_ci @options[:has_suite_setup] ||= (source =~ /void\s+suiteSetUp\s*\(/) 23135375f98Sopenharmony_ci @options[:has_suite_teardown] ||= (source =~ /int\s+suiteTearDown\s*\(int\s+([a-zA-Z0-9_])+\s*\)/) 23235375f98Sopenharmony_ci end 23335375f98Sopenharmony_ci 23435375f98Sopenharmony_ci def create_header(output, mocks, testfile_includes = []) 23535375f98Sopenharmony_ci output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') 23635375f98Sopenharmony_ci output.puts("\n/*=======Automagically Detected Files To Include=====*/") 23735375f98Sopenharmony_ci output.puts('extern "C" {') if @options[:externcincludes] 23835375f98Sopenharmony_ci output.puts("#include \"#{@options[:framework]}.h\"") 23935375f98Sopenharmony_ci output.puts('#include "cmock.h"') unless mocks.empty? 24035375f98Sopenharmony_ci output.puts('}') if @options[:externcincludes] 24135375f98Sopenharmony_ci if @options[:defines] && !@options[:defines].empty? 24235375f98Sopenharmony_ci output.puts("/* injected defines for unity settings, etc */") 24335375f98Sopenharmony_ci @options[:defines].each do |d| 24435375f98Sopenharmony_ci def_only = d.match(/(\w+).*/)[1] 24535375f98Sopenharmony_ci output.puts("#ifndef #{def_only}\n#define #{d}\n#endif /* #{def_only} */") 24635375f98Sopenharmony_ci end 24735375f98Sopenharmony_ci end 24835375f98Sopenharmony_ci if @options[:header_file] && !@options[:header_file].empty? 24935375f98Sopenharmony_ci output.puts("#include \"#{File.basename(@options[:header_file])}\"") 25035375f98Sopenharmony_ci else 25135375f98Sopenharmony_ci @options[:includes].flatten.uniq.compact.each do |inc| 25235375f98Sopenharmony_ci output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") 25335375f98Sopenharmony_ci end 25435375f98Sopenharmony_ci testfile_includes.each do |inc| 25535375f98Sopenharmony_ci output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") 25635375f98Sopenharmony_ci end 25735375f98Sopenharmony_ci end 25835375f98Sopenharmony_ci output.puts('extern "C" {') if @options[:externcincludes] 25935375f98Sopenharmony_ci mocks.each do |mock| 26035375f98Sopenharmony_ci output.puts("#include \"#{mock}\"") 26135375f98Sopenharmony_ci end 26235375f98Sopenharmony_ci output.puts('}') if @options[:externcincludes] 26335375f98Sopenharmony_ci output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception) 26435375f98Sopenharmony_ci 26535375f98Sopenharmony_ci return unless @options[:enforce_strict_ordering] 26635375f98Sopenharmony_ci 26735375f98Sopenharmony_ci output.puts('') 26835375f98Sopenharmony_ci output.puts('int GlobalExpectCount;') 26935375f98Sopenharmony_ci output.puts('int GlobalVerifyOrder;') 27035375f98Sopenharmony_ci output.puts('char* GlobalOrderError;') 27135375f98Sopenharmony_ci end 27235375f98Sopenharmony_ci 27335375f98Sopenharmony_ci def create_externs(output, tests, _mocks) 27435375f98Sopenharmony_ci output.puts("\n/*=======External Functions This Runner Calls=====*/") 27535375f98Sopenharmony_ci output.puts("extern void #{@options[:setup_name]}(void);") 27635375f98Sopenharmony_ci output.puts("extern void #{@options[:teardown_name]}(void);") 27735375f98Sopenharmony_ci output.puts("\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif") if @options[:externc] 27835375f98Sopenharmony_ci tests.each do |test| 27935375f98Sopenharmony_ci output.puts("extern void #{test[:test]}(#{test[:call] || 'void'});") 28035375f98Sopenharmony_ci end 28135375f98Sopenharmony_ci output.puts("#ifdef __cplusplus\n}\n#endif") if @options[:externc] 28235375f98Sopenharmony_ci output.puts('') 28335375f98Sopenharmony_ci end 28435375f98Sopenharmony_ci 28535375f98Sopenharmony_ci def create_mock_management(output, mock_headers) 28635375f98Sopenharmony_ci output.puts("\n/*=======Mock Management=====*/") 28735375f98Sopenharmony_ci output.puts('static void CMock_Init(void)') 28835375f98Sopenharmony_ci output.puts('{') 28935375f98Sopenharmony_ci 29035375f98Sopenharmony_ci if @options[:enforce_strict_ordering] 29135375f98Sopenharmony_ci output.puts(' GlobalExpectCount = 0;') 29235375f98Sopenharmony_ci output.puts(' GlobalVerifyOrder = 0;') 29335375f98Sopenharmony_ci output.puts(' GlobalOrderError = NULL;') 29435375f98Sopenharmony_ci end 29535375f98Sopenharmony_ci 29635375f98Sopenharmony_ci mocks = mock_headers.map { |mock| File.basename(mock, '.*') } 29735375f98Sopenharmony_ci mocks.each do |mock| 29835375f98Sopenharmony_ci mock_clean = TypeSanitizer.sanitize_c_identifier(mock) 29935375f98Sopenharmony_ci output.puts(" #{mock_clean}_Init();") 30035375f98Sopenharmony_ci end 30135375f98Sopenharmony_ci output.puts("}\n") 30235375f98Sopenharmony_ci 30335375f98Sopenharmony_ci output.puts('static void CMock_Verify(void)') 30435375f98Sopenharmony_ci output.puts('{') 30535375f98Sopenharmony_ci mocks.each do |mock| 30635375f98Sopenharmony_ci mock_clean = TypeSanitizer.sanitize_c_identifier(mock) 30735375f98Sopenharmony_ci output.puts(" #{mock_clean}_Verify();") 30835375f98Sopenharmony_ci end 30935375f98Sopenharmony_ci output.puts("}\n") 31035375f98Sopenharmony_ci 31135375f98Sopenharmony_ci output.puts('static void CMock_Destroy(void)') 31235375f98Sopenharmony_ci output.puts('{') 31335375f98Sopenharmony_ci mocks.each do |mock| 31435375f98Sopenharmony_ci mock_clean = TypeSanitizer.sanitize_c_identifier(mock) 31535375f98Sopenharmony_ci output.puts(" #{mock_clean}_Destroy();") 31635375f98Sopenharmony_ci end 31735375f98Sopenharmony_ci output.puts("}\n") 31835375f98Sopenharmony_ci end 31935375f98Sopenharmony_ci 32035375f98Sopenharmony_ci def create_setup(output) 32135375f98Sopenharmony_ci return if @options[:has_setup] 32235375f98Sopenharmony_ci 32335375f98Sopenharmony_ci output.puts("\n/*=======Setup (stub)=====*/") 32435375f98Sopenharmony_ci output.puts("void #{@options[:setup_name]}(void) {}") 32535375f98Sopenharmony_ci end 32635375f98Sopenharmony_ci 32735375f98Sopenharmony_ci def create_teardown(output) 32835375f98Sopenharmony_ci return if @options[:has_teardown] 32935375f98Sopenharmony_ci 33035375f98Sopenharmony_ci output.puts("\n/*=======Teardown (stub)=====*/") 33135375f98Sopenharmony_ci output.puts("void #{@options[:teardown_name]}(void) {}") 33235375f98Sopenharmony_ci end 33335375f98Sopenharmony_ci 33435375f98Sopenharmony_ci def create_suite_setup(output) 33535375f98Sopenharmony_ci return if @options[:suite_setup].nil? 33635375f98Sopenharmony_ci 33735375f98Sopenharmony_ci output.puts("\n/*=======Suite Setup=====*/") 33835375f98Sopenharmony_ci output.puts('void suiteSetUp(void)') 33935375f98Sopenharmony_ci output.puts('{') 34035375f98Sopenharmony_ci output.puts(@options[:suite_setup]) 34135375f98Sopenharmony_ci output.puts('}') 34235375f98Sopenharmony_ci end 34335375f98Sopenharmony_ci 34435375f98Sopenharmony_ci def create_suite_teardown(output) 34535375f98Sopenharmony_ci return if @options[:suite_teardown].nil? 34635375f98Sopenharmony_ci 34735375f98Sopenharmony_ci output.puts("\n/*=======Suite Teardown=====*/") 34835375f98Sopenharmony_ci output.puts('int suiteTearDown(int num_failures)') 34935375f98Sopenharmony_ci output.puts('{') 35035375f98Sopenharmony_ci output.puts(@options[:suite_teardown]) 35135375f98Sopenharmony_ci output.puts('}') 35235375f98Sopenharmony_ci end 35335375f98Sopenharmony_ci 35435375f98Sopenharmony_ci def create_reset(output) 35535375f98Sopenharmony_ci output.puts("\n/*=======Test Reset Options=====*/") 35635375f98Sopenharmony_ci output.puts("void #{@options[:test_reset_name]}(void);") 35735375f98Sopenharmony_ci output.puts("void #{@options[:test_reset_name]}(void)") 35835375f98Sopenharmony_ci output.puts('{') 35935375f98Sopenharmony_ci output.puts(" #{@options[:teardown_name]}();") 36035375f98Sopenharmony_ci output.puts(' CMock_Verify();') 36135375f98Sopenharmony_ci output.puts(' CMock_Destroy();') 36235375f98Sopenharmony_ci output.puts(' CMock_Init();') 36335375f98Sopenharmony_ci output.puts(" #{@options[:setup_name]}();") 36435375f98Sopenharmony_ci output.puts('}') 36535375f98Sopenharmony_ci output.puts("void #{@options[:test_verify_name]}(void);") 36635375f98Sopenharmony_ci output.puts("void #{@options[:test_verify_name]}(void)") 36735375f98Sopenharmony_ci output.puts('{') 36835375f98Sopenharmony_ci output.puts(' CMock_Verify();') 36935375f98Sopenharmony_ci output.puts('}') 37035375f98Sopenharmony_ci end 37135375f98Sopenharmony_ci 37235375f98Sopenharmony_ci def create_run_test(output) 37335375f98Sopenharmony_ci require 'erb' 37435375f98Sopenharmony_ci file = File.read(File.join(__dir__, 'run_test.erb')) 37535375f98Sopenharmony_ci template = ERB.new(file, trim_mode: '<>') 37635375f98Sopenharmony_ci output.puts("\n#{template.result(binding)}") 37735375f98Sopenharmony_ci end 37835375f98Sopenharmony_ci 37935375f98Sopenharmony_ci def create_args_wrappers(output, tests) 38035375f98Sopenharmony_ci return unless @options[:use_param_tests] 38135375f98Sopenharmony_ci 38235375f98Sopenharmony_ci output.puts("\n/*=======Parameterized Test Wrappers=====*/") 38335375f98Sopenharmony_ci tests.each do |test| 38435375f98Sopenharmony_ci next if test[:args].nil? || test[:args].empty? 38535375f98Sopenharmony_ci 38635375f98Sopenharmony_ci test[:args].each.with_index(1) do |args, idx| 38735375f98Sopenharmony_ci output.puts("static void runner_args#{idx}_#{test[:test]}(void)") 38835375f98Sopenharmony_ci output.puts('{') 38935375f98Sopenharmony_ci output.puts(" #{test[:test]}(#{args});") 39035375f98Sopenharmony_ci output.puts("}\n") 39135375f98Sopenharmony_ci end 39235375f98Sopenharmony_ci end 39335375f98Sopenharmony_ci end 39435375f98Sopenharmony_ci 39535375f98Sopenharmony_ci def create_main(output, filename, tests, used_mocks) 39635375f98Sopenharmony_ci output.puts("\n/*=======MAIN=====*/") 39735375f98Sopenharmony_ci main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s 39835375f98Sopenharmony_ci if @options[:cmdline_args] 39935375f98Sopenharmony_ci if main_name != 'main' 40035375f98Sopenharmony_ci output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv);") 40135375f98Sopenharmony_ci end 40235375f98Sopenharmony_ci output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv)") 40335375f98Sopenharmony_ci output.puts('{') 40435375f98Sopenharmony_ci output.puts(' int parse_status = UnityParseOptions(argc, argv);') 40535375f98Sopenharmony_ci output.puts(' if (parse_status != 0)') 40635375f98Sopenharmony_ci output.puts(' {') 40735375f98Sopenharmony_ci output.puts(' if (parse_status < 0)') 40835375f98Sopenharmony_ci output.puts(' {') 40935375f98Sopenharmony_ci output.puts(" UnityPrint(\"#{filename.gsub('.c', '').gsub(/\\/, '\\\\\\')}.\");") 41035375f98Sopenharmony_ci output.puts(' UNITY_PRINT_EOL();') 41135375f98Sopenharmony_ci tests.each do |test| 41235375f98Sopenharmony_ci if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty? 41335375f98Sopenharmony_ci output.puts(" UnityPrint(\" #{test[:test]}\");") 41435375f98Sopenharmony_ci output.puts(' UNITY_PRINT_EOL();') 41535375f98Sopenharmony_ci else 41635375f98Sopenharmony_ci test[:args].each do |args| 41735375f98Sopenharmony_ci output.puts(" UnityPrint(\" #{test[:test]}(#{args})\");") 41835375f98Sopenharmony_ci output.puts(' UNITY_PRINT_EOL();') 41935375f98Sopenharmony_ci end 42035375f98Sopenharmony_ci end 42135375f98Sopenharmony_ci end 42235375f98Sopenharmony_ci output.puts(' return 0;') 42335375f98Sopenharmony_ci output.puts(' }') 42435375f98Sopenharmony_ci output.puts(' return parse_status;') 42535375f98Sopenharmony_ci output.puts(' }') 42635375f98Sopenharmony_ci else 42735375f98Sopenharmony_ci main_return = @options[:omit_begin_end] ? 'void' : 'int' 42835375f98Sopenharmony_ci if main_name != 'main' 42935375f98Sopenharmony_ci output.puts("#{@options[:main_export_decl]} #{main_return} #{main_name}(void);") 43035375f98Sopenharmony_ci end 43135375f98Sopenharmony_ci output.puts("#{main_return} #{main_name}(void)") 43235375f98Sopenharmony_ci output.puts('{') 43335375f98Sopenharmony_ci end 43435375f98Sopenharmony_ci output.puts(' suiteSetUp();') if @options[:has_suite_setup] 43535375f98Sopenharmony_ci if @options[:omit_begin_end] 43635375f98Sopenharmony_ci output.puts(" UnitySetTestFile(\"#{filename.gsub(/\\/, '\\\\\\')}\");") 43735375f98Sopenharmony_ci else 43835375f98Sopenharmony_ci output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");") 43935375f98Sopenharmony_ci end 44035375f98Sopenharmony_ci tests.each do |test| 44135375f98Sopenharmony_ci if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty? 44235375f98Sopenharmony_ci output.puts(" run_test(#{test[:test]}, \"#{test[:test]}\", #{test[:line_number]});") 44335375f98Sopenharmony_ci else 44435375f98Sopenharmony_ci test[:args].each.with_index(1) do |args, idx| 44535375f98Sopenharmony_ci wrapper = "runner_args#{idx}_#{test[:test]}" 44635375f98Sopenharmony_ci testname = "#{test[:test]}(#{args})".dump 44735375f98Sopenharmony_ci output.puts(" run_test(#{wrapper}, #{testname}, #{test[:line_number]});") 44835375f98Sopenharmony_ci end 44935375f98Sopenharmony_ci end 45035375f98Sopenharmony_ci end 45135375f98Sopenharmony_ci output.puts 45235375f98Sopenharmony_ci output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty? 45335375f98Sopenharmony_ci if @options[:has_suite_teardown] 45435375f98Sopenharmony_ci if @options[:omit_begin_end] 45535375f98Sopenharmony_ci output.puts(' (void) suite_teardown(0);') 45635375f98Sopenharmony_ci else 45735375f98Sopenharmony_ci output.puts(' return suiteTearDown(UnityEnd());') 45835375f98Sopenharmony_ci end 45935375f98Sopenharmony_ci else 46035375f98Sopenharmony_ci output.puts(' return UnityEnd();') unless @options[:omit_begin_end] 46135375f98Sopenharmony_ci end 46235375f98Sopenharmony_ci output.puts('}') 46335375f98Sopenharmony_ci end 46435375f98Sopenharmony_ci 46535375f98Sopenharmony_ci def create_h_file(output, filename, tests, testfile_includes, used_mocks) 46635375f98Sopenharmony_ci filename = File.basename(filename).gsub(/[-\/\\.,\s]/, '_').upcase 46735375f98Sopenharmony_ci output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') 46835375f98Sopenharmony_ci output.puts("#ifndef _#{filename}") 46935375f98Sopenharmony_ci output.puts("#define _#{filename}\n\n") 47035375f98Sopenharmony_ci output.puts("#include \"#{@options[:framework]}.h\"") 47135375f98Sopenharmony_ci output.puts('#include "cmock.h"') unless used_mocks.empty? 47235375f98Sopenharmony_ci @options[:includes].flatten.uniq.compact.each do |inc| 47335375f98Sopenharmony_ci output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") 47435375f98Sopenharmony_ci end 47535375f98Sopenharmony_ci testfile_includes.each do |inc| 47635375f98Sopenharmony_ci output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}") 47735375f98Sopenharmony_ci end 47835375f98Sopenharmony_ci output.puts "\n" 47935375f98Sopenharmony_ci tests.each do |test| 48035375f98Sopenharmony_ci if test[:params].nil? || test[:params].empty? 48135375f98Sopenharmony_ci output.puts("void #{test[:test]}(void);") 48235375f98Sopenharmony_ci else 48335375f98Sopenharmony_ci output.puts("void #{test[:test]}(#{test[:params]});") 48435375f98Sopenharmony_ci end 48535375f98Sopenharmony_ci end 48635375f98Sopenharmony_ci output.puts("#endif\n\n") 48735375f98Sopenharmony_ci end 48835375f98Sopenharmony_ciend 48935375f98Sopenharmony_ci 49035375f98Sopenharmony_ciif $0 == __FILE__ 49135375f98Sopenharmony_ci options = { includes: [] } 49235375f98Sopenharmony_ci 49335375f98Sopenharmony_ci # parse out all the options first (these will all be removed as we go) 49435375f98Sopenharmony_ci ARGV.reject! do |arg| 49535375f98Sopenharmony_ci case arg 49635375f98Sopenharmony_ci when '-cexception' 49735375f98Sopenharmony_ci options[:plugins] = [:cexception] 49835375f98Sopenharmony_ci true 49935375f98Sopenharmony_ci when '-externcincludes' 50035375f98Sopenharmony_ci options[:externcincludes] = true 50135375f98Sopenharmony_ci true 50235375f98Sopenharmony_ci when /\.*\.ya?ml$/ 50335375f98Sopenharmony_ci options = UnityTestRunnerGenerator.grab_config(arg) 50435375f98Sopenharmony_ci true 50535375f98Sopenharmony_ci when /--(\w+)="?(.*)"?/ 50635375f98Sopenharmony_ci options[Regexp.last_match(1).to_sym] = Regexp.last_match(2) 50735375f98Sopenharmony_ci true 50835375f98Sopenharmony_ci when /\.*\.(?:hpp|hh|H|h)$/ 50935375f98Sopenharmony_ci options[:includes] << arg 51035375f98Sopenharmony_ci true 51135375f98Sopenharmony_ci else false 51235375f98Sopenharmony_ci end 51335375f98Sopenharmony_ci end 51435375f98Sopenharmony_ci 51535375f98Sopenharmony_ci # make sure there is at least one parameter left (the input file) 51635375f98Sopenharmony_ci unless ARGV[0] 51735375f98Sopenharmony_ci puts ["\nusage: ruby #{__FILE__} (files) (options) input_test_file (output)", 51835375f98Sopenharmony_ci "\n input_test_file - this is the C file you want to create a runner for", 51935375f98Sopenharmony_ci ' output - this is the name of the runner file to generate', 52035375f98Sopenharmony_ci ' defaults to (input_test_file)_Runner', 52135375f98Sopenharmony_ci ' files:', 52235375f98Sopenharmony_ci ' *.yml / *.yaml - loads configuration from here in :unity or :cmock', 52335375f98Sopenharmony_ci ' *.h - header files are added as #includes in runner', 52435375f98Sopenharmony_ci ' options:', 52535375f98Sopenharmony_ci ' -cexception - include cexception support', 52635375f98Sopenharmony_ci ' -externc - add extern "C" for cpp support', 52735375f98Sopenharmony_ci ' --setup_name="" - redefine setUp func name to something else', 52835375f98Sopenharmony_ci ' --teardown_name="" - redefine tearDown func name to something else', 52935375f98Sopenharmony_ci ' --main_name="" - redefine main func name to something else', 53035375f98Sopenharmony_ci ' --test_prefix="" - redefine test prefix from default test|spec|should', 53135375f98Sopenharmony_ci ' --test_reset_name="" - redefine resetTest func name to something else', 53235375f98Sopenharmony_ci ' --test_verify_name="" - redefine verifyTest func name to something else', 53335375f98Sopenharmony_ci ' --suite_setup="" - code to execute for setup of entire suite', 53435375f98Sopenharmony_ci ' --suite_teardown="" - code to execute for teardown of entire suite', 53535375f98Sopenharmony_ci ' --use_param_tests=1 - enable parameterized tests (disabled by default)', 53635375f98Sopenharmony_ci ' --omit_begin_end=1 - omit calls to UnityBegin and UnityEnd (disabled by default)', 53735375f98Sopenharmony_ci ' --header_file="" - path/name of test header file to generate too'].join("\n") 53835375f98Sopenharmony_ci exit 1 53935375f98Sopenharmony_ci end 54035375f98Sopenharmony_ci 54135375f98Sopenharmony_ci # create the default test runner name if not specified 54235375f98Sopenharmony_ci ARGV[1] = ARGV[0].gsub('.c', '_Runner.c') unless ARGV[1] 54335375f98Sopenharmony_ci 54435375f98Sopenharmony_ci UnityTestRunnerGenerator.new(options).run(ARGV[0], ARGV[1]) 54535375f98Sopenharmony_ciend 546