summaryrefslogtreecommitdiff
path: root/deps/Unity/auto
diff options
context:
space:
mode:
Diffstat (limited to 'deps/Unity/auto')
-rw-r--r--deps/Unity/auto/__init__.py0
-rw-r--r--deps/Unity/auto/colour_prompt.rb119
-rw-r--r--deps/Unity/auto/colour_reporter.rb39
-rwxr-xr-xdeps/Unity/auto/extract_version.py15
-rw-r--r--deps/Unity/auto/generate_config.yml36
-rw-r--r--deps/Unity/auto/generate_module.rb317
-rwxr-xr-xdeps/Unity/auto/generate_test_runner.rb545
-rw-r--r--deps/Unity/auto/parse_output.rb383
-rw-r--r--deps/Unity/auto/run_test.erb37
-rw-r--r--deps/Unity/auto/stylize_as_junit.py161
-rwxr-xr-xdeps/Unity/auto/stylize_as_junit.rb251
-rw-r--r--deps/Unity/auto/test_file_filter.rb27
-rw-r--r--deps/Unity/auto/type_sanitizer.rb6
-rw-r--r--deps/Unity/auto/unity_test_summary.py139
-rw-r--r--deps/Unity/auto/unity_test_summary.rb139
-rw-r--r--deps/Unity/auto/yaml_helper.rb22
16 files changed, 2236 insertions, 0 deletions
diff --git a/deps/Unity/auto/__init__.py b/deps/Unity/auto/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/deps/Unity/auto/__init__.py
diff --git a/deps/Unity/auto/colour_prompt.rb b/deps/Unity/auto/colour_prompt.rb
new file mode 100644
index 0000000..85cbfd8
--- /dev/null
+++ b/deps/Unity/auto/colour_prompt.rb
@@ -0,0 +1,119 @@
+# ==========================================
+# 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]
+# ==========================================
+
+if RUBY_PLATFORM =~ /(win|w)32$/
+ begin
+ require 'Win32API'
+ rescue LoadError
+ puts 'ERROR! "Win32API" library not found'
+ puts '"Win32API" is required for colour on a windows machine'
+ puts ' try => "gem install Win32API" on the command line'
+ puts
+ end
+ # puts
+ # puts 'Windows Environment Detected...'
+ # puts 'Win32API Library Found.'
+ # puts
+end
+
+class ColourCommandLine
+ def initialize
+ return unless RUBY_PLATFORM =~ /(win|w)32$/
+
+ get_std_handle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L')
+ @set_console_txt_attrb =
+ Win32API.new('kernel32', 'SetConsoleTextAttribute', %w[L N], 'I')
+ @hout = get_std_handle.call(-11)
+ end
+
+ def change_to(new_colour)
+ if RUBY_PLATFORM =~ /(win|w)32$/
+ @set_console_txt_attrb.call(@hout, win32_colour(new_colour))
+ else
+ "\033[30;#{posix_colour(new_colour)};22m"
+ end
+ end
+
+ def win32_colour(colour)
+ case colour
+ when :black then 0
+ when :dark_blue then 1
+ when :dark_green then 2
+ when :dark_cyan then 3
+ when :dark_red then 4
+ when :dark_purple then 5
+ when :dark_yellow, :narrative then 6
+ when :default_white, :default, :dark_white then 7
+ when :silver then 8
+ when :blue then 9
+ when :green, :success then 10
+ when :cyan, :output then 11
+ when :red, :failure then 12
+ when :purple then 13
+ when :yellow then 14
+ when :white then 15
+ else
+ 0
+ end
+ end
+
+ def posix_colour(colour)
+ # ANSI Escape Codes - Foreground colors
+ # | Code | Color |
+ # | 39 | Default foreground color |
+ # | 30 | Black |
+ # | 31 | Red |
+ # | 32 | Green |
+ # | 33 | Yellow |
+ # | 34 | Blue |
+ # | 35 | Magenta |
+ # | 36 | Cyan |
+ # | 37 | Light gray |
+ # | 90 | Dark gray |
+ # | 91 | Light red |
+ # | 92 | Light green |
+ # | 93 | Light yellow |
+ # | 94 | Light blue |
+ # | 95 | Light magenta |
+ # | 96 | Light cyan |
+ # | 97 | White |
+
+ case colour
+ when :black then 30
+ when :red, :failure then 31
+ when :green, :success then 32
+ when :yellow then 33
+ when :blue, :narrative then 34
+ when :purple, :magenta then 35
+ when :cyan, :output then 36
+ when :white, :default_white then 37
+ when :default then 39
+ else
+ 39
+ end
+ end
+
+ def out_c(mode, colour, str)
+ case RUBY_PLATFORM
+ when /(win|w)32$/
+ change_to(colour)
+ $stdout.puts str if mode == :puts
+ $stdout.print str if mode == :print
+ change_to(:default_white)
+ else
+ $stdout.puts("#{change_to(colour)}#{str}\033[0m") if mode == :puts
+ $stdout.print("#{change_to(colour)}#{str}\033[0m") if mode == :print
+ end
+ end
+end
+
+def colour_puts(role, str)
+ ColourCommandLine.new.out_c(:puts, role, str)
+end
+
+def colour_print(role, str)
+ ColourCommandLine.new.out_c(:print, role, str)
+end
diff --git a/deps/Unity/auto/colour_reporter.rb b/deps/Unity/auto/colour_reporter.rb
new file mode 100644
index 0000000..b86b76c
--- /dev/null
+++ b/deps/Unity/auto/colour_reporter.rb
@@ -0,0 +1,39 @@
+# ==========================================
+# 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_relative 'colour_prompt'
+
+$colour_output = true
+
+def report(message)
+ if !$colour_output
+ $stdout.puts(message)
+ else
+ message = message.join('\n') if message.instance_of?(Array)
+ message.each_line do |line|
+ line.chomp!
+ colour = case line
+ when /(?:total\s+)?tests:?\s+(\d+)\s+(?:total\s+)?failures:?\s+\d+\s+Ignored:?/i
+ Regexp.last_match(1).to_i.zero? ? :green : :red
+ when /PASS/
+ :green
+ when /^OK$/
+ :green
+ when /(?:FAIL|ERROR)/
+ :red
+ when /IGNORE/
+ :yellow
+ when /^(?:Creating|Compiling|Linking)/
+ :white
+ else
+ :silver
+ end
+ colour_puts(colour, line)
+ end
+ end
+ $stdout.flush
+ $stderr.flush
+end
diff --git a/deps/Unity/auto/extract_version.py b/deps/Unity/auto/extract_version.py
new file mode 100755
index 0000000..1d137e5
--- /dev/null
+++ b/deps/Unity/auto/extract_version.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+import re
+import sys
+
+ver_re = re.compile(r"^#define\s+UNITY_VERSION_(?:MAJOR|MINOR|BUILD)\s+(\d+)$")
+version = []
+
+with open(sys.argv[1], "r") as f:
+ for line in f:
+ m = ver_re.match(line)
+ if m:
+ version.append(m.group(1))
+
+print(".".join(version))
+
diff --git a/deps/Unity/auto/generate_config.yml b/deps/Unity/auto/generate_config.yml
new file mode 100644
index 0000000..4a5e474
--- /dev/null
+++ b/deps/Unity/auto/generate_config.yml
@@ -0,0 +1,36 @@
+#this is a sample configuration file for generate_module
+#you would use it by calling generate_module with the -ygenerate_config.yml option
+#files like this are useful for customizing generate_module to your environment
+:generate_module:
+ :defaults:
+ #these defaults are used in place of any missing options at the command line
+ :path_src: ../src/
+ :path_inc: ../src/
+ :path_tst: ../test/
+ :update_svn: true
+ :includes:
+ #use [] for no additional includes, otherwise list the includes on separate lines
+ :src:
+ - Defs.h
+ - Board.h
+ :inc: []
+ :tst:
+ - Defs.h
+ - Board.h
+ - Exception.h
+ :boilerplates:
+ #these are inserted at the top of generated files.
+ #just comment out or remove if not desired.
+ #use %1$s where you would like the file name to appear (path/extension not included)
+ :src: |
+ //-------------------------------------------
+ // %1$s.c
+ //-------------------------------------------
+ :inc: |
+ //-------------------------------------------
+ // %1$s.h
+ //-------------------------------------------
+ :tst: |
+ //-------------------------------------------
+ // Test%1$s.c : Units tests for %1$s.c
+ //-------------------------------------------
diff --git a/deps/Unity/auto/generate_module.rb b/deps/Unity/auto/generate_module.rb
new file mode 100644
index 0000000..7b33c72
--- /dev/null
+++ b/deps/Unity/auto/generate_module.rb
@@ -0,0 +1,317 @@
+# ==========================================
+# 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]
+# ==========================================
+
+# This script creates all the files with start code necessary for a new module.
+# A simple module only requires a source file, header file, and test file.
+# Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware).
+
+require 'rubygems'
+require 'fileutils'
+require 'pathname'
+
+# TEMPLATE_TST
+TEMPLATE_TST ||= '#ifdef %5$s
+
+#include "unity.h"
+
+%2$s#include "%1$s.h"
+
+void setUp(void)
+{
+}
+
+void tearDown(void)
+{
+}
+
+void test_%4$s_NeedToImplement(void)
+{
+ TEST_IGNORE_MESSAGE("Need to Implement %1$s");
+}
+
+#endif // %5$s
+'.freeze
+
+# TEMPLATE_SRC
+TEMPLATE_SRC ||= '%2$s#include "%1$s.h"
+'.freeze
+
+# TEMPLATE_INC
+TEMPLATE_INC ||= '#ifndef %3$s_H
+#define %3$s_H
+%2$s
+
+#endif // %3$s_H
+'.freeze
+
+class UnityModuleGenerator
+ ############################
+ def initialize(options = nil)
+ @options = UnityModuleGenerator.default_options
+ case options
+ when NilClass then @options
+ when String then @options.merge!(UnityModuleGenerator.grab_config(options))
+ when Hash then @options.merge!(options)
+ else raise 'If you specify arguments, it should be a filename or a hash of options'
+ end
+
+ # Create default file paths if none were provided
+ @options[:path_src] = "#{__dir__}/../src/" if @options[:path_src].nil?
+ @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil?
+ @options[:path_tst] = "#{__dir__}/../test/" if @options[:path_tst].nil?
+ @options[:path_src] += '/' unless @options[:path_src][-1] == 47
+ @options[:path_inc] += '/' unless @options[:path_inc][-1] == 47
+ @options[:path_tst] += '/' unless @options[:path_tst][-1] == 47
+
+ # Built in patterns
+ @patterns = {
+ 'src' => {
+ '' => { inc: [] }
+ },
+ 'test' => {
+ '' => { inc: [] }
+ },
+ 'dh' => {
+ 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h')] },
+ 'Hardware' => { inc: [] }
+ },
+ 'dih' => {
+ 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h'), create_filename('%1$s', 'Interrupt.h')] },
+ 'Interrupt' => { inc: [create_filename('%1$s', 'Hardware.h')] },
+ 'Hardware' => { inc: [] }
+ },
+ 'mch' => {
+ 'Model' => { inc: [] },
+ 'Conductor' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'Hardware.h')] },
+ 'Hardware' => { inc: [] }
+ },
+ 'mvp' => {
+ 'Model' => { inc: [] },
+ 'Presenter' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'View.h')] },
+ 'View' => { inc: [] }
+ }
+ }
+ end
+
+ ############################
+ def self.default_options
+ {
+ pattern: 'src',
+ includes: {
+ src: [],
+ inc: [],
+ tst: []
+ },
+ update_svn: false,
+ boilerplates: {},
+ test_prefix: 'Test',
+ mock_prefix: 'Mock',
+ test_define: 'TEST'
+ }
+ end
+
+ ############################
+ def self.grab_config(config_file)
+ options = default_options
+ unless config_file.nil? || config_file.empty?
+ require_relative 'yaml_helper'
+ yaml_guts = YamlHelper.load_file(config_file)
+ options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
+ raise "No :unity or :cmock section found in #{config_file}" unless options
+ end
+ options
+ end
+
+ ############################
+ def files_to_operate_on(module_name, pattern = nil)
+ # strip any leading path information from the module name and save for later
+ subfolder = File.dirname(module_name)
+ module_name = File.basename(module_name)
+
+ # create triad definition
+ prefix = @options[:test_prefix] || 'Test'
+ triad = [{ ext: '.c', path: @options[:path_src], prefix: '', template: TEMPLATE_SRC, inc: :src, boilerplate: @options[:boilerplates][:src] },
+ { ext: '.h', path: @options[:path_inc], prefix: '', template: TEMPLATE_INC, inc: :inc, boilerplate: @options[:boilerplates][:inc] },
+ { ext: '.c', path: @options[:path_tst], prefix: prefix, template: TEMPLATE_TST, inc: :tst, boilerplate: @options[:boilerplates][:tst], test_define: @options[:test_define] }]
+
+ # prepare the pattern for use
+ pattern = (pattern || @options[:pattern] || 'src').downcase
+ patterns = @patterns[pattern]
+ raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil?
+
+ # single file patterns (currently just 'test') can reject the other parts of the triad
+ triad.select! { |v| v[:inc] == :tst } if pattern == 'test'
+
+ # Assemble the path/names of the files we need to work with.
+ files = []
+ triad.each do |cfg|
+ patterns.each_pair do |pattern_file, pattern_traits|
+ submodule_name = create_filename(module_name, pattern_file)
+ filename = cfg[:prefix] + submodule_name + cfg[:ext]
+ files << {
+ path: (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath,
+ name: submodule_name,
+ template: cfg[:template],
+ test_define: cfg[:test_define],
+ boilerplate: cfg[:boilerplate],
+ includes: case (cfg[:inc])
+ when :src then (@options[:includes][:src] || []) | (pattern_traits[:inc].map { |f| format(f, module_name) })
+ when :inc then (@options[:includes][:inc] || [])
+ when :tst then (@options[:includes][:tst] || []) | (pattern_traits[:inc].map { |f| format("#{@options[:mock_prefix]}#{f}", module_name) })
+ end
+ }
+ end
+ end
+
+ files
+ end
+
+ ############################
+ def neutralize_filename(name, start_cap: true)
+ return name if name.empty?
+
+ name = name.split(/(?:\s+|_|(?=[A-Z][a-z]))|(?<=[a-z])(?=[A-Z])/).map(&:capitalize).join('_')
+ name = name[0].downcase + name[1..] unless start_cap
+ name
+ end
+
+ ############################
+ def create_filename(part1, part2 = '')
+ name = part2.empty? ? part1 : "#{part1}_#{part2}"
+ case (@options[:naming])
+ when 'bumpy' then neutralize_filename(name, start_cap: false).delete('_')
+ when 'camel' then neutralize_filename(name).delete('_')
+ when 'snake' then neutralize_filename(name).downcase
+ when 'caps' then neutralize_filename(name).upcase
+ else name
+ end
+ end
+
+ ############################
+ def generate(module_name, pattern = nil)
+ files = files_to_operate_on(module_name, pattern)
+
+ # Abort if all of the module files already exist
+ all_files_exist = true
+ files.each do |file|
+ all_files_exist = false unless File.exist?(file[:path])
+ end
+ raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist
+
+ # Create Source Modules
+ files.each_with_index do |file, _i|
+ # If this file already exists, don't overwrite it.
+ if File.exist?(file[:path])
+ puts "File #{file[:path]} already exists!"
+ next
+ end
+ # Create the path first if necessary.
+ FileUtils.mkdir_p(File.dirname(file[:path]), verbose: false)
+ File.open(file[:path], 'w') do |f|
+ f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
+ f.write(file[:template] % [file[:name],
+ file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join,
+ file[:name].upcase.tr('-', '_'),
+ file[:name].tr('-', '_'),
+ file[:test_define]])
+ end
+ if @options[:update_svn]
+ `svn add \"#{file[:path]}\"`
+ if $!.exitstatus.zero?
+ puts "File #{file[:path]} created and added to source control"
+ else
+ puts "File #{file[:path]} created but FAILED adding to source control!"
+ end
+ else
+ puts "File #{file[:path]} created"
+ end
+ end
+ puts 'Generate Complete'
+ end
+
+ ############################
+ def destroy(module_name, pattern = nil)
+ files_to_operate_on(module_name, pattern).each do |filespec|
+ file = filespec[:path]
+ if File.exist?(file)
+ if @options[:update_svn]
+ `svn delete \"#{file}\" --force`
+ puts "File #{file} deleted and removed from source control"
+ else
+ FileUtils.remove(file)
+ puts "File #{file} deleted"
+ end
+ else
+ puts "File #{file} does not exist so cannot be removed."
+ end
+ end
+ puts 'Destroy Complete'
+ end
+end
+
+############################
+# Handle As Command Line If Called That Way
+if $0 == __FILE__
+ destroy = false
+ options = {}
+ module_name = nil
+
+ # Parse the command line parameters.
+ ARGV.each do |arg|
+ case arg
+ when /^-d/ then destroy = true
+ when /^-u/ then options[:update_svn] = true
+ when /^-p"?(\w+)"?/ then options[:pattern] = Regexp.last_match(1)
+ when /^-s"?(.+)"?/ then options[:path_src] = Regexp.last_match(1)
+ when /^-i"?(.+)"?/ then options[:path_inc] = Regexp.last_match(1)
+ when /^-t"?(.+)"?/ then options[:path_tst] = Regexp.last_match(1)
+ when /^-n"?(.+)"?/ then options[:naming] = Regexp.last_match(1)
+ when /^-y"?(.+)"?/ then options = UnityModuleGenerator.grab_config(Regexp.last_match(1))
+ when /^(\w+)/
+ raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil?
+
+ module_name = arg
+ when /^-(h|-help)/
+ ARGV = [].freeze
+ else
+ raise "ERROR: Unknown option specified '#{arg}'"
+ end
+ end
+
+ unless ARGV[0]
+ puts ["\nGENERATE MODULE\n-------- ------",
+ "\nUsage: ruby generate_module [options] module_name",
+ " -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)",
+ " -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)",
+ " -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)",
+ ' -p"MCH" sets the output pattern to MCH.',
+ ' dh - driver hardware.',
+ ' dih - driver interrupt hardware.',
+ ' mch - model conductor hardware.',
+ ' mvp - model view presenter.',
+ ' src - just a source module, header and test. (DEFAULT)',
+ ' test - just a test file.',
+ ' -d destroy module instead of creating it.',
+ ' -n"camel" sets the file naming convention.',
+ ' bumpy - BumpyCaseFilenames.',
+ ' camel - camelCaseFilenames.',
+ ' snake - snake_case_filenames.',
+ ' caps - CAPS_CASE_FILENAMES.',
+ ' -u update subversion too (requires subversion command line)',
+ ' -y"my.yml" selects a different yaml config file for module generation',
+ ''].join("\n")
+ exit
+ end
+
+ raise 'ERROR: You must have a Module name specified! (use option -h for help)' if module_name.nil?
+
+ if destroy
+ UnityModuleGenerator.new(options).destroy(module_name)
+ else
+ UnityModuleGenerator.new(options).generate(module_name)
+ end
+
+end
diff --git a/deps/Unity/auto/generate_test_runner.rb b/deps/Unity/auto/generate_test_runner.rb
new file mode 100755
index 0000000..102f6f3
--- /dev/null
+++ b/deps/Unity/auto/generate_test_runner.rb
@@ -0,0 +1,545 @@
+#!/usr/bin/ruby
+
+# ==========================================
+# 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]
+# ==========================================
+
+class UnityTestRunnerGenerator
+ def initialize(options = nil)
+ @options = UnityTestRunnerGenerator.default_options
+ case options
+ when NilClass
+ @options
+ when String
+ @options.merge!(UnityTestRunnerGenerator.grab_config(options))
+ when Hash
+ # Check if some of these have been specified
+ @options[:has_setup] = !options[:setup_name].nil?
+ @options[:has_teardown] = !options[:teardown_name].nil?
+ @options[:has_suite_setup] = !options[:suite_setup].nil?
+ @options[:has_suite_teardown] = !options[:suite_teardown].nil?
+ @options.merge!(options)
+ else
+ raise 'If you specify arguments, it should be a filename or a hash of options'
+ end
+ require_relative 'type_sanitizer'
+ end
+
+ def self.default_options
+ {
+ includes: [],
+ defines: [],
+ plugins: [],
+ framework: :unity,
+ test_prefix: 'test|spec|should',
+ mock_prefix: 'Mock',
+ mock_suffix: '',
+ setup_name: 'setUp',
+ teardown_name: 'tearDown',
+ test_reset_name: 'resetTest',
+ test_verify_name: 'verifyTest',
+ main_name: 'main', # set to :auto to automatically generate each time
+ main_export_decl: '',
+ cmdline_args: false,
+ omit_begin_end: false,
+ use_param_tests: false,
+ use_system_files: true,
+ include_extensions: '(?:hpp|hh|H|h)',
+ source_extensions: '(?:cpp|cc|ino|C|c)'
+ }
+ end
+
+ def self.grab_config(config_file)
+ options = default_options
+ unless config_file.nil? || config_file.empty?
+ require_relative 'yaml_helper'
+ yaml_guts = YamlHelper.load_file(config_file)
+ options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
+ raise "No :unity or :cmock section found in #{config_file}" unless options
+ end
+ options
+ end
+
+ def run(input_file, output_file, options = nil)
+ @options.merge!(options) unless options.nil?
+
+ # pull required data from source file
+ source = File.read(input_file)
+ source = source.force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
+ tests = find_tests(source)
+ headers = find_includes(source)
+ testfile_includes = @options[:use_system_files] ? (headers[:local] + headers[:system]) : (headers[:local])
+ used_mocks = find_mocks(testfile_includes)
+ testfile_includes = (testfile_includes - used_mocks)
+ testfile_includes.delete_if { |inc| inc =~ /(unity|cmock)/ }
+ find_setup_and_teardown(source)
+
+ # build runner file
+ generate(input_file, output_file, tests, used_mocks, testfile_includes)
+
+ # determine which files were used to return them
+ all_files_used = [input_file, output_file]
+ all_files_used += testfile_includes.map { |filename| "#{filename}.c" } unless testfile_includes.empty?
+ all_files_used += @options[:includes] unless @options[:includes].empty?
+ all_files_used += headers[:linkonly] unless headers[:linkonly].empty?
+ all_files_used.uniq
+ end
+
+ def generate(input_file, output_file, tests, used_mocks, testfile_includes)
+ File.open(output_file, 'w') do |output|
+ create_header(output, used_mocks, testfile_includes)
+ create_externs(output, tests, used_mocks)
+ create_mock_management(output, used_mocks)
+ create_setup(output)
+ create_teardown(output)
+ create_suite_setup(output)
+ create_suite_teardown(output)
+ create_reset(output)
+ create_run_test(output) unless tests.empty?
+ create_args_wrappers(output, tests)
+ create_main(output, input_file, tests, used_mocks)
+ end
+
+ return unless @options[:header_file] && !@options[:header_file].empty?
+
+ File.open(@options[:header_file], 'w') do |output|
+ create_h_file(output, @options[:header_file], tests, testfile_includes, used_mocks)
+ end
+ end
+
+ def find_tests(source)
+ tests_and_line_numbers = []
+
+ # contains characters which will be substituted from within strings, doing
+ # this prevents these characters from interfering with scrubbers
+ # @ is not a valid C character, so there should be no clashes with files genuinely containing these markers
+ substring_subs = { '{' => '@co@', '}' => '@cc@', ';' => '@ss@', '/' => '@fs@' }
+ substring_re = Regexp.union(substring_subs.keys)
+ substring_unsubs = substring_subs.invert # the inverse map will be used to fix the strings afterwords
+ substring_unsubs['@quote@'] = '\\"'
+ substring_unsubs['@apos@'] = '\\\''
+ substring_unre = Regexp.union(substring_unsubs.keys)
+ source_scrubbed = source.clone
+ source_scrubbed = source_scrubbed.gsub(/\\"/, '@quote@') # hide escaped quotes to allow capture of the full string/char
+ source_scrubbed = source_scrubbed.gsub(/\\'/, '@apos@') # hide escaped apostrophes to allow capture of the full string/char
+ source_scrubbed = source_scrubbed.gsub(/("[^"\n]*")|('[^'\n]*')/) { |s| s.gsub(substring_re, substring_subs) } # temporarily hide problematic characters within strings
+ source_scrubbed = source_scrubbed.gsub(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks
+ source_scrubbed = source_scrubbed.gsub(/\/\*.*?\*\//m, '') # remove block comments
+ source_scrubbed = source_scrubbed.gsub(/\/\/.*$/, '') # remove line comments (all that remain)
+ lines = source_scrubbed.split(/(^\s*\#.*$) | (;|\{|\}) /x) # Treat preprocessor directives as a logical line. Match ;, {, and } as end of lines
+ .map { |line| line.gsub(substring_unre, substring_unsubs) } # unhide the problematic characters previously removed
+
+ lines.each_with_index do |line, _index|
+ # find tests
+ next unless line =~ /^((?:\s*(?:TEST_(?:CASE|RANGE|MATRIX))\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/m
+ next unless line =~ /^((?:\s*(?:TEST_(?:CASE|RANGE|MATRIX))\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]})\w*)\s*\(\s*(.*)\s*\)/m
+
+ arguments = Regexp.last_match(1)
+ name = Regexp.last_match(2)
+ call = Regexp.last_match(3)
+ params = Regexp.last_match(4)
+ args = nil
+
+ if @options[:use_param_tests] && !arguments.empty?
+ args = []
+ type_and_args = arguments.split(/TEST_(CASE|RANGE|MATRIX)/)
+ (1...type_and_args.length).step(2).each do |i|
+ case type_and_args[i]
+ when 'CASE'
+ args << type_and_args[i + 1].sub(/^\s*\(\s*(.*?)\s*\)\s*$/m, '\1')
+
+ when 'RANGE'
+ 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|
+ exclude_end = arg_values_str[0] == '<' && arg_values_str[-1] == '>'
+ arg_values_str[1...-1].map do |arg_value_str|
+ arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i
+ end.push(exclude_end)
+ end.map do |arg_values|
+ Range.new(arg_values[0], arg_values[1], arg_values[3]).step(arg_values[2]).to_a
+ end.reduce(nil) do |result, arg_range_expanded|
+ result.nil? ? arg_range_expanded.map { |a| [a] } : result.product(arg_range_expanded)
+ end.map do |arg_combinations|
+ arg_combinations.flatten.join(', ')
+ end
+
+ when 'MATRIX'
+ single_arg_regex_string = /(?:(?:"(?:\\"|[^\\])*?")+|(?:'\\?.')+|(?:[^\s\]\["',]|\[[\d\S_-]+\])+)/.source
+ args_regex = /\[((?:\s*#{single_arg_regex_string}\s*,?)*(?:\s*#{single_arg_regex_string})?\s*)\]/m
+ arg_elements_regex = /\s*(#{single_arg_regex_string})\s*,\s*/m
+
+ args += type_and_args[i + 1].scan(args_regex).flatten.map do |arg_values_str|
+ ("#{arg_values_str},").scan(arg_elements_regex)
+ end.reduce do |result, arg_range_expanded|
+ result.product(arg_range_expanded)
+ end.map do |arg_combinations|
+ arg_combinations.flatten.join(', ')
+ end
+ end
+ end
+ end
+
+ tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 }
+ end
+
+ tests_and_line_numbers.uniq! { |v| v[:test] }
+
+ # determine line numbers and create tests to run
+ source_lines = source.split("\n")
+ source_index = 0
+ tests_and_line_numbers.size.times do |i|
+ source_lines[source_index..].each_with_index do |line, index|
+ next unless line =~ /\s+#{tests_and_line_numbers[i][:test]}(?:\s|\()/
+
+ source_index += index
+ tests_and_line_numbers[i][:line_number] = source_index + 1
+ break
+ end
+ end
+
+ tests_and_line_numbers
+ end
+
+ def find_includes(source)
+ # remove comments (block and line, in three steps to ensure correct precedence)
+ source.gsub!(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks
+ source.gsub!(/\/\*.*?\*\//m, '') # remove block comments
+ source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain)
+
+ # parse out includes
+ {
+ local: source.scan(/^\s*#include\s+"\s*(.+\.#{@options[:include_extensions]})\s*"/).flatten,
+ system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" },
+ linkonly: source.scan(/^TEST_SOURCE_FILE\(\s*"\s*(.+\.#{@options[:source_extensions]})\s*"/).flatten
+ }
+ end
+
+ def find_mocks(includes)
+ mock_headers = []
+ includes.each do |include_path|
+ include_file = File.basename(include_path)
+ mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}.*#{@options[:mock_suffix]}\.h$/i
+ end
+ mock_headers
+ end
+
+ def find_setup_and_teardown(source)
+ @options[:has_setup] = source =~ /void\s+#{@options[:setup_name]}\s*\(/
+ @options[:has_teardown] = source =~ /void\s+#{@options[:teardown_name]}\s*\(/
+ @options[:has_suite_setup] ||= (source =~ /void\s+suiteSetUp\s*\(/)
+ @options[:has_suite_teardown] ||= (source =~ /int\s+suiteTearDown\s*\(int\s+([a-zA-Z0-9_])+\s*\)/)
+ end
+
+ def create_header(output, mocks, testfile_includes = [])
+ output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */')
+ output.puts("\n/*=======Automagically Detected Files To Include=====*/")
+ output.puts('extern "C" {') if @options[:externcincludes]
+ output.puts("#include \"#{@options[:framework]}.h\"")
+ output.puts('#include "cmock.h"') unless mocks.empty?
+ output.puts('}') if @options[:externcincludes]
+ if @options[:defines] && !@options[:defines].empty?
+ output.puts("/* injected defines for unity settings, etc */")
+ @options[:defines].each do |d|
+ def_only = d.match(/(\w+).*/)[1]
+ output.puts("#ifndef #{def_only}\n#define #{d}\n#endif /* #{def_only} */")
+ end
+ end
+ if @options[:header_file] && !@options[:header_file].empty?
+ output.puts("#include \"#{File.basename(@options[:header_file])}\"")
+ else
+ @options[:includes].flatten.uniq.compact.each do |inc|
+ output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
+ end
+ testfile_includes.each do |inc|
+ output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
+ end
+ end
+ output.puts('extern "C" {') if @options[:externcincludes]
+ mocks.each do |mock|
+ output.puts("#include \"#{mock}\"")
+ end
+ output.puts('}') if @options[:externcincludes]
+ output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception)
+
+ return unless @options[:enforce_strict_ordering]
+
+ output.puts('')
+ output.puts('int GlobalExpectCount;')
+ output.puts('int GlobalVerifyOrder;')
+ output.puts('char* GlobalOrderError;')
+ end
+
+ def create_externs(output, tests, _mocks)
+ output.puts("\n/*=======External Functions This Runner Calls=====*/")
+ output.puts("extern void #{@options[:setup_name]}(void);")
+ output.puts("extern void #{@options[:teardown_name]}(void);")
+ output.puts("\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif") if @options[:externc]
+ tests.each do |test|
+ output.puts("extern void #{test[:test]}(#{test[:call] || 'void'});")
+ end
+ output.puts("#ifdef __cplusplus\n}\n#endif") if @options[:externc]
+ output.puts('')
+ end
+
+ def create_mock_management(output, mock_headers)
+ output.puts("\n/*=======Mock Management=====*/")
+ output.puts('static void CMock_Init(void)')
+ output.puts('{')
+
+ if @options[:enforce_strict_ordering]
+ output.puts(' GlobalExpectCount = 0;')
+ output.puts(' GlobalVerifyOrder = 0;')
+ output.puts(' GlobalOrderError = NULL;')
+ end
+
+ mocks = mock_headers.map { |mock| File.basename(mock, '.*') }
+ mocks.each do |mock|
+ mock_clean = TypeSanitizer.sanitize_c_identifier(mock)
+ output.puts(" #{mock_clean}_Init();")
+ end
+ output.puts("}\n")
+
+ output.puts('static void CMock_Verify(void)')
+ output.puts('{')
+ mocks.each do |mock|
+ mock_clean = TypeSanitizer.sanitize_c_identifier(mock)
+ output.puts(" #{mock_clean}_Verify();")
+ end
+ output.puts("}\n")
+
+ output.puts('static void CMock_Destroy(void)')
+ output.puts('{')
+ mocks.each do |mock|
+ mock_clean = TypeSanitizer.sanitize_c_identifier(mock)
+ output.puts(" #{mock_clean}_Destroy();")
+ end
+ output.puts("}\n")
+ end
+
+ def create_setup(output)
+ return if @options[:has_setup]
+
+ output.puts("\n/*=======Setup (stub)=====*/")
+ output.puts("void #{@options[:setup_name]}(void) {}")
+ end
+
+ def create_teardown(output)
+ return if @options[:has_teardown]
+
+ output.puts("\n/*=======Teardown (stub)=====*/")
+ output.puts("void #{@options[:teardown_name]}(void) {}")
+ end
+
+ def create_suite_setup(output)
+ return if @options[:suite_setup].nil?
+
+ output.puts("\n/*=======Suite Setup=====*/")
+ output.puts('void suiteSetUp(void)')
+ output.puts('{')
+ output.puts(@options[:suite_setup])
+ output.puts('}')
+ end
+
+ def create_suite_teardown(output)
+ return if @options[:suite_teardown].nil?
+
+ output.puts("\n/*=======Suite Teardown=====*/")
+ output.puts('int suiteTearDown(int num_failures)')
+ output.puts('{')
+ output.puts(@options[:suite_teardown])
+ output.puts('}')
+ end
+
+ def create_reset(output)
+ output.puts("\n/*=======Test Reset Options=====*/")
+ output.puts("void #{@options[:test_reset_name]}(void);")
+ output.puts("void #{@options[:test_reset_name]}(void)")
+ output.puts('{')
+ output.puts(" #{@options[:teardown_name]}();")
+ output.puts(' CMock_Verify();')
+ output.puts(' CMock_Destroy();')
+ output.puts(' CMock_Init();')
+ output.puts(" #{@options[:setup_name]}();")
+ output.puts('}')
+ output.puts("void #{@options[:test_verify_name]}(void);")
+ output.puts("void #{@options[:test_verify_name]}(void)")
+ output.puts('{')
+ output.puts(' CMock_Verify();')
+ output.puts('}')
+ end
+
+ def create_run_test(output)
+ require 'erb'
+ file = File.read(File.join(__dir__, 'run_test.erb'))
+ template = ERB.new(file, trim_mode: '<>')
+ output.puts("\n#{template.result(binding)}")
+ end
+
+ def create_args_wrappers(output, tests)
+ return unless @options[:use_param_tests]
+
+ output.puts("\n/*=======Parameterized Test Wrappers=====*/")
+ tests.each do |test|
+ next if test[:args].nil? || test[:args].empty?
+
+ test[:args].each.with_index(1) do |args, idx|
+ output.puts("static void runner_args#{idx}_#{test[:test]}(void)")
+ output.puts('{')
+ output.puts(" #{test[:test]}(#{args});")
+ output.puts("}\n")
+ end
+ end
+ end
+
+ def create_main(output, filename, tests, used_mocks)
+ output.puts("\n/*=======MAIN=====*/")
+ main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s
+ if @options[:cmdline_args]
+ if main_name != 'main'
+ output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv);")
+ end
+ output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv)")
+ output.puts('{')
+ output.puts(' int parse_status = UnityParseOptions(argc, argv);')
+ output.puts(' if (parse_status != 0)')
+ output.puts(' {')
+ output.puts(' if (parse_status < 0)')
+ output.puts(' {')
+ output.puts(" UnityPrint(\"#{filename.gsub('.c', '').gsub(/\\/, '\\\\\\')}.\");")
+ output.puts(' UNITY_PRINT_EOL();')
+ tests.each do |test|
+ if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty?
+ output.puts(" UnityPrint(\" #{test[:test]}\");")
+ output.puts(' UNITY_PRINT_EOL();')
+ else
+ test[:args].each do |args|
+ output.puts(" UnityPrint(\" #{test[:test]}(#{args})\");")
+ output.puts(' UNITY_PRINT_EOL();')
+ end
+ end
+ end
+ output.puts(' return 0;')
+ output.puts(' }')
+ output.puts(' return parse_status;')
+ output.puts(' }')
+ else
+ main_return = @options[:omit_begin_end] ? 'void' : 'int'
+ if main_name != 'main'
+ output.puts("#{@options[:main_export_decl]} #{main_return} #{main_name}(void);")
+ end
+ output.puts("#{main_return} #{main_name}(void)")
+ output.puts('{')
+ end
+ output.puts(' suiteSetUp();') if @options[:has_suite_setup]
+ if @options[:omit_begin_end]
+ output.puts(" UnitySetTestFile(\"#{filename.gsub(/\\/, '\\\\\\')}\");")
+ else
+ output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");")
+ end
+ tests.each do |test|
+ if (!@options[:use_param_tests]) || test[:args].nil? || test[:args].empty?
+ output.puts(" run_test(#{test[:test]}, \"#{test[:test]}\", #{test[:line_number]});")
+ else
+ test[:args].each.with_index(1) do |args, idx|
+ wrapper = "runner_args#{idx}_#{test[:test]}"
+ testname = "#{test[:test]}(#{args})".dump
+ output.puts(" run_test(#{wrapper}, #{testname}, #{test[:line_number]});")
+ end
+ end
+ end
+ output.puts
+ output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty?
+ if @options[:has_suite_teardown]
+ if @options[:omit_begin_end]
+ output.puts(' (void) suite_teardown(0);')
+ else
+ output.puts(' return suiteTearDown(UnityEnd());')
+ end
+ else
+ output.puts(' return UnityEnd();') unless @options[:omit_begin_end]
+ end
+ output.puts('}')
+ end
+
+ def create_h_file(output, filename, tests, testfile_includes, used_mocks)
+ filename = File.basename(filename).gsub(/[-\/\\.,\s]/, '_').upcase
+ output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */')
+ output.puts("#ifndef _#{filename}")
+ output.puts("#define _#{filename}\n\n")
+ output.puts("#include \"#{@options[:framework]}.h\"")
+ output.puts('#include "cmock.h"') unless used_mocks.empty?
+ @options[:includes].flatten.uniq.compact.each do |inc|
+ output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
+ end
+ testfile_includes.each do |inc|
+ output.puts("#include #{inc.include?('<') ? inc : "\"#{inc}\""}")
+ end
+ output.puts "\n"
+ tests.each do |test|
+ if test[:params].nil? || test[:params].empty?
+ output.puts("void #{test[:test]}(void);")
+ else
+ output.puts("void #{test[:test]}(#{test[:params]});")
+ end
+ end
+ output.puts("#endif\n\n")
+ end
+end
+
+if $0 == __FILE__
+ options = { includes: [] }
+
+ # parse out all the options first (these will all be removed as we go)
+ ARGV.reject! do |arg|
+ case arg
+ when '-cexception'
+ options[:plugins] = [:cexception]
+ true
+ when '-externcincludes'
+ options[:externcincludes] = true
+ true
+ when /\.*\.ya?ml$/
+ options = UnityTestRunnerGenerator.grab_config(arg)
+ true
+ when /--(\w+)="?(.*)"?/
+ options[Regexp.last_match(1).to_sym] = Regexp.last_match(2)
+ true
+ when /\.*\.(?:hpp|hh|H|h)$/
+ options[:includes] << arg
+ true
+ else false
+ end
+ end
+
+ # make sure there is at least one parameter left (the input file)
+ unless ARGV[0]
+ puts ["\nusage: ruby #{__FILE__} (files) (options) input_test_file (output)",
+ "\n input_test_file - this is the C file you want to create a runner for",
+ ' output - this is the name of the runner file to generate',
+ ' defaults to (input_test_file)_Runner',
+ ' files:',
+ ' *.yml / *.yaml - loads configuration from here in :unity or :cmock',
+ ' *.h - header files are added as #includes in runner',
+ ' options:',
+ ' -cexception - include cexception support',
+ ' -externc - add extern "C" for cpp support',
+ ' --setup_name="" - redefine setUp func name to something else',
+ ' --teardown_name="" - redefine tearDown func name to something else',
+ ' --main_name="" - redefine main func name to something else',
+ ' --test_prefix="" - redefine test prefix from default test|spec|should',
+ ' --test_reset_name="" - redefine resetTest func name to something else',
+ ' --test_verify_name="" - redefine verifyTest func name to something else',
+ ' --suite_setup="" - code to execute for setup of entire suite',
+ ' --suite_teardown="" - code to execute for teardown of entire suite',
+ ' --use_param_tests=1 - enable parameterized tests (disabled by default)',
+ ' --omit_begin_end=1 - omit calls to UnityBegin and UnityEnd (disabled by default)',
+ ' --header_file="" - path/name of test header file to generate too'].join("\n")
+ exit 1
+ end
+
+ # create the default test runner name if not specified
+ ARGV[1] = ARGV[0].gsub('.c', '_Runner.c') unless ARGV[1]
+
+ UnityTestRunnerGenerator.new(options).run(ARGV[0], ARGV[1])
+end
diff --git a/deps/Unity/auto/parse_output.rb b/deps/Unity/auto/parse_output.rb
new file mode 100644
index 0000000..aa306e2
--- /dev/null
+++ b/deps/Unity/auto/parse_output.rb
@@ -0,0 +1,383 @@
+#============================================================
+# Author: John Theofanopoulos
+# A simple parser. Takes the output files generated during the
+# build process and extracts information relating to the tests.
+#
+# Notes:
+# To capture an output file under VS builds use the following:
+# devenv [build instructions] > Output.txt & type Output.txt
+#
+# To capture an output file under Linux builds use the following:
+# make | tee Output.txt
+#
+# This script can handle the following output formats:
+# - normal output (raw unity)
+# - fixture output (unity_fixture.h/.c)
+# - fixture output with verbose flag set ("-v")
+# - time output flag set (UNITY_INCLUDE_EXEC_TIME define enabled with milliseconds output)
+#
+# To use this parser use the following command
+# ruby parseOutput.rb [options] [file]
+# options: -xml : produce a JUnit compatible XML file
+# -suiteRequiredSuiteName
+# : replace default test suite name to
+# "RequiredSuiteName" (can be any name)
+# file: file to scan for results
+#============================================================
+
+# Parser class for handling the input file
+class ParseOutput
+ def initialize
+ # internal data
+ @class_name_idx = 0
+ @result_usual_idx = 3
+ @path_delim = nil
+
+ # xml output related
+ @xml_out = false
+ @array_list = false
+
+ # current suite name and statistics
+ ## testsuite name
+ @real_test_suite_name = 'Unity'
+ ## classname for testcase
+ @test_suite = nil
+ @total_tests = 0
+ @test_passed = 0
+ @test_failed = 0
+ @test_ignored = 0
+ end
+
+ # Set the flag to indicate if there will be an XML output file or not
+ def set_xml_output
+ @xml_out = true
+ end
+
+ # Set the flag to indicate if there will be an XML output file or not
+ def test_suite_name=(cli_arg)
+ @real_test_suite_name = cli_arg
+ puts "Real test suite name will be '#{@real_test_suite_name}'"
+ end
+
+ def xml_encode_s(str)
+ str.encode(:xml => :attr)
+ end
+
+ # If write our output to XML
+ def write_xml_output
+ output = File.open('report.xml', 'w')
+ output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ @array_list.each do |item|
+ output << item << "\n"
+ end
+ end
+
+ # Pushes the suite info as xml to the array list, which will be written later
+ def push_xml_output_suite_info
+ # Insert opening tag at front
+ heading = "<testsuite name=#{xml_encode_s(@real_test_suite_name)} tests=\"#{@total_tests}\" failures=\"#{@test_failed}\" skips=\"#{@test_ignored}\">"
+ @array_list.insert(0, heading)
+ # Push back the closing tag
+ @array_list.push '</testsuite>'
+ end
+
+ # Pushes xml output data to the array list, which will be written later
+ def push_xml_output_passed(test_name, execution_time = 0)
+ @array_list.push " <testcase classname=#{xml_encode_s(@test_suite)} name=#{xml_encode_s(test_name)} time=#{xml_encode_s((execution_time / 1000.0).to_s)} />"
+ end
+
+ # Pushes xml output data to the array list, which will be written later
+ def push_xml_output_failed(test_name, reason, execution_time = 0)
+ @array_list.push " <testcase classname=#{xml_encode_s(@test_suite)} name=#{xml_encode_s(test_name)} time=#{xml_encode_s((execution_time / 1000.0).to_s)} >"
+ @array_list.push " <failure type=\"ASSERT FAILED\">#{reason}</failure>"
+ @array_list.push ' </testcase>'
+ end
+
+ # Pushes xml output data to the array list, which will be written later
+ def push_xml_output_ignored(test_name, reason, execution_time = 0)
+ @array_list.push " <testcase classname=#{xml_encode_s(@test_suite)} name=#{xml_encode_s(test_name)} time=#{xml_encode_s((execution_time / 1000.0).to_s)} >"
+ @array_list.push " <skipped type=\"TEST IGNORED\">#{reason}</skipped>"
+ @array_list.push ' </testcase>'
+ end
+
+ # This function will try and determine when the suite is changed. This is
+ # is the name that gets added to the classname parameter.
+ def test_suite_verify(test_suite_name)
+ # Split the path name
+ test_name = test_suite_name.split(@path_delim)
+
+ # Remove the extension and extract the base_name
+ base_name = test_name[test_name.size - 1].split('.')[0]
+
+ # Return if the test suite hasn't changed
+ return unless base_name.to_s != @test_suite.to_s
+
+ @test_suite = base_name
+ printf "New Test: %s\n", @test_suite
+ end
+
+ # Prepares the line for verbose fixture output ("-v")
+ def prepare_fixture_line(line)
+ line = line.sub('IGNORE_TEST(', '')
+ line = line.sub('TEST(', '')
+ line = line.sub(')', ',')
+ line = line.chomp
+ array = line.split(',')
+ array.map { |x| x.to_s.lstrip.chomp }
+ end
+
+ # Test was flagged as having passed so format the output.
+ # This is using the Unity fixture output and not the original Unity output.
+ def test_passed_unity_fixture(array)
+ class_name = array[0]
+ test_name = array[1]
+ test_suite_verify(class_name)
+ printf "%-40s PASS\n", test_name
+
+ push_xml_output_passed(test_name) if @xml_out
+ end
+
+ # Test was flagged as having failed so format the output.
+ # This is using the Unity fixture output and not the original Unity output.
+ def test_failed_unity_fixture(array)
+ class_name = array[0]
+ test_name = array[1]
+ test_suite_verify(class_name)
+ reason_array = array[2].split(':')
+ reason = "#{reason_array[-1].lstrip.chomp} at line: #{reason_array[-4]}"
+
+ printf "%-40s FAILED\n", test_name
+
+ push_xml_output_failed(test_name, reason) if @xml_out
+ end
+
+ # Test was flagged as being ignored so format the output.
+ # This is using the Unity fixture output and not the original Unity output.
+ def test_ignored_unity_fixture(array)
+ class_name = array[0]
+ test_name = array[1]
+ reason = 'No reason given'
+ if array.size > 2
+ reason_array = array[2].split(':')
+ tmp_reason = reason_array[-1].lstrip.chomp
+ reason = tmp_reason == 'IGNORE' ? 'No reason given' : tmp_reason
+ end
+ test_suite_verify(class_name)
+ printf "%-40s IGNORED\n", test_name
+
+ push_xml_output_ignored(test_name, reason) if @xml_out
+ end
+
+ # Test was flagged as having passed so format the output
+ def test_passed(array)
+ # ':' symbol will be valid in function args now
+ real_method_name = array[@result_usual_idx - 1..-2].join(':')
+ array = array[0..@result_usual_idx - 2] + [real_method_name] + [array[-1]]
+
+ last_item = array.length - 1
+ test_time = get_test_time(array[last_item])
+ test_name = array[last_item - 1]
+ test_suite_verify(array[@class_name_idx])
+ printf "%-40s PASS %10d ms\n", test_name, test_time
+
+ return unless @xml_out
+
+ push_xml_output_passed(test_name, test_time) if @xml_out
+ end
+
+ # Test was flagged as having failed so format the line
+ def test_failed(array)
+ # ':' symbol will be valid in function args now
+ real_method_name = array[@result_usual_idx - 1..-3].join(':')
+ array = array[0..@result_usual_idx - 3] + [real_method_name] + array[-2..]
+
+ last_item = array.length - 1
+ test_time = get_test_time(array[last_item])
+ test_name = array[last_item - 2]
+ reason = "#{array[last_item].chomp.lstrip} at line: #{array[last_item - 3]}"
+ class_name = array[@class_name_idx]
+
+ if test_name.start_with? 'TEST('
+ array2 = test_name.split(' ')
+
+ test_suite = array2[0].sub('TEST(', '')
+ test_suite = test_suite.sub(',', '')
+ class_name = test_suite
+
+ test_name = array2[1].sub(')', '')
+ end
+
+ test_suite_verify(class_name)
+ printf "%-40s FAILED %10d ms\n", test_name, test_time
+
+ push_xml_output_failed(test_name, reason, test_time) if @xml_out
+ end
+
+ # Test was flagged as being ignored so format the output
+ def test_ignored(array)
+ # ':' symbol will be valid in function args now
+ real_method_name = array[@result_usual_idx - 1..-3].join(':')
+ array = array[0..@result_usual_idx - 3] + [real_method_name] + array[-2..]
+
+ last_item = array.length - 1
+ test_time = get_test_time(array[last_item])
+ test_name = array[last_item - 2]
+ reason = array[last_item].chomp.lstrip
+ class_name = array[@class_name_idx]
+
+ if test_name.start_with? 'TEST('
+ array2 = test_name.split(' ')
+
+ test_suite = array2[0].sub('TEST(', '')
+ test_suite = test_suite.sub(',', '')
+ class_name = test_suite
+
+ test_name = array2[1].sub(')', '')
+ end
+
+ test_suite_verify(class_name)
+ printf "%-40s IGNORED %10d ms\n", test_name, test_time
+
+ push_xml_output_ignored(test_name, reason, test_time) if @xml_out
+ end
+
+ # Test time will be in ms
+ def get_test_time(value_with_time)
+ test_time_array = value_with_time.scan(/\((-?\d+.?\d*) ms\)\s*$/).flatten.map do |arg_value_str|
+ arg_value_str.include?('.') ? arg_value_str.to_f : arg_value_str.to_i
+ end
+
+ test_time_array.any? ? test_time_array[0] : 0
+ end
+
+ # Adjusts the os specific members according to the current path style
+ # (Windows or Unix based)
+ def detect_os_specifics(line)
+ if line.include? '\\'
+ # Windows X:\Y\Z
+ @class_name_idx = 1
+ @path_delim = '\\'
+ else
+ # Unix Based /X/Y/Z
+ @class_name_idx = 0
+ @path_delim = '/'
+ end
+ end
+
+ # Main function used to parse the file that was captured.
+ def process(file_name)
+ @array_list = []
+
+ puts "Parsing file: #{file_name}"
+
+ @test_passed = 0
+ @test_failed = 0
+ @test_ignored = 0
+ puts ''
+ puts '=================== RESULTS ====================='
+ puts ''
+ # Apply binary encoding. Bad symbols will be unchanged
+ File.open(file_name, 'rb').each do |line|
+ # Typical test lines look like these:
+ # ----------------------------------------------------
+ # 1. normal output:
+ # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
+ # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
+ # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
+ #
+ # 2. fixture output
+ # <path>/<test_file>.c:63:TEST(<test_group>, <test_function>):FAIL: Expected 0x00001234 Was 0x00005A5A
+ # <path>/<test_file>.c:36:TEST(<test_group>, <test_function>):IGNORE
+ # Note: "PASS" information won't be generated in this mode
+ #
+ # 3. fixture output with verbose information ("-v")
+ # TEST(<test_group, <test_file>)<path>/<test_file>:168::FAIL: Expected 0x8D Was 0x8C
+ # TEST(<test_group>, <test_file>)<path>/<test_file>:22::IGNORE: This Test Was Ignored On Purpose
+ # IGNORE_TEST(<test_group, <test_file>)
+ # TEST(<test_group, <test_file>) PASS
+ #
+ # Note: Where path is different on Unix vs Windows devices (Windows leads with a drive letter)!
+ detect_os_specifics(line)
+ line_array = line.split(':')
+
+ # If we were able to split the line then we can look to see if any of our target words
+ # were found. Case is important.
+ next unless (line_array.size >= 4) || (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(')
+
+ # check if the output is fixture output (with verbose flag "-v")
+ if (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(')
+ line_array = prepare_fixture_line(line)
+ if line.include? ' PASS'
+ test_passed_unity_fixture(line_array)
+ @test_passed += 1
+ elsif line.include? 'FAIL'
+ test_failed_unity_fixture(line_array)
+ @test_failed += 1
+ elsif line.include? 'IGNORE'
+ test_ignored_unity_fixture(line_array)
+ @test_ignored += 1
+ end
+ # normal output / fixture output (without verbose "-v")
+ elsif line.include? ':PASS'
+ test_passed(line_array)
+ @test_passed += 1
+ elsif line.include? ':FAIL'
+ test_failed(line_array)
+ @test_failed += 1
+ elsif line.include? ':IGNORE:'
+ test_ignored(line_array)
+ @test_ignored += 1
+ elsif line.include? ':IGNORE'
+ line_array.push('No reason given')
+ test_ignored(line_array)
+ @test_ignored += 1
+ elsif line_array.size >= 4
+ # We will check output from color compilation
+ if line_array[@result_usual_idx..].any? { |l| l.include? 'PASS' }
+ test_passed(line_array)
+ @test_passed += 1
+ elsif line_array[@result_usual_idx..].any? { |l| l.include? 'FAIL' }
+ test_failed(line_array)
+ @test_failed += 1
+ elsif line_array[@result_usual_idx..-2].any? { |l| l.include? 'IGNORE' }
+ test_ignored(line_array)
+ @test_ignored += 1
+ elsif line_array[@result_usual_idx..].any? { |l| l.include? 'IGNORE' }
+ line_array.push("No reason given (#{get_test_time(line_array[@result_usual_idx..])} ms)")
+ test_ignored(line_array)
+ @test_ignored += 1
+ end
+ end
+ @total_tests = @test_passed + @test_failed + @test_ignored
+ end
+ puts ''
+ puts '=================== SUMMARY ====================='
+ puts ''
+ puts "Tests Passed : #{@test_passed}"
+ puts "Tests Failed : #{@test_failed}"
+ puts "Tests Ignored : #{@test_ignored}"
+
+ return unless @xml_out
+
+ # push information about the suite
+ push_xml_output_suite_info
+ # write xml output file
+ write_xml_output
+ end
+end
+
+# If the command line has no values in, used a default value of Output.txt
+parse_my_file = ParseOutput.new
+
+if ARGV.size >= 1
+ ARGV.each do |arg|
+ if arg == '-xml'
+ parse_my_file.set_xml_output
+ elsif arg.start_with?('-suite')
+ parse_my_file.test_suite_name = arg.delete_prefix('-suite')
+ else
+ parse_my_file.process(arg)
+ break
+ end
+ end
+end
diff --git a/deps/Unity/auto/run_test.erb b/deps/Unity/auto/run_test.erb
new file mode 100644
index 0000000..68b3373
--- /dev/null
+++ b/deps/Unity/auto/run_test.erb
@@ -0,0 +1,37 @@
+/*=======Test Runner Used To Run Each Test=====*/
+static void run_test(UnityTestFunction func, const char* name, UNITY_LINE_TYPE line_num)
+{
+ Unity.CurrentTestName = name;
+ Unity.CurrentTestLineNumber = line_num;
+#ifdef UNITY_USE_COMMAND_LINE_ARGS
+ if (!UnityTestMatches())
+ return;
+#endif
+ Unity.NumberOfTests++;
+ UNITY_CLR_DETAILS();
+ UNITY_EXEC_TIME_START();
+ CMock_Init();
+ if (TEST_PROTECT())
+ {
+<% if @options[:plugins].include?(:cexception) %>
+ volatile CEXCEPTION_T e;
+ Try {
+ <%= @options[:setup_name] %>();
+ func();
+ } Catch(e) {
+ TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!");
+ }
+<% else %>
+ <%= @options[:setup_name] %>();
+ func();
+<% end %>
+ }
+ if (TEST_PROTECT())
+ {
+ <%= @options[:teardown_name] %>();
+ CMock_Verify();
+ }
+ CMock_Destroy();
+ UNITY_EXEC_TIME_STOP();
+ UnityConcludeTest();
+}
diff --git a/deps/Unity/auto/stylize_as_junit.py b/deps/Unity/auto/stylize_as_junit.py
new file mode 100644
index 0000000..06c8659
--- /dev/null
+++ b/deps/Unity/auto/stylize_as_junit.py
@@ -0,0 +1,161 @@
+#! python3
+# ==========================================
+# Fork from Unity Project - A Test Framework for C
+# Pull request on Gerrit in progress, the objective of this file is to be deleted when official Unity deliveries
+# include that modification
+# Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de
+# [Released under MIT License. Please refer to license.txt for details]
+# ==========================================
+import sys
+import os
+from glob import glob
+import argparse
+
+from pyparsing import *
+from junit_xml import TestSuite, TestCase
+
+
+class UnityTestSummary:
+ def __init__(self):
+ self.report = ''
+ self.total_tests = 0
+ self.failures = 0
+ self.ignored = 0
+ self.targets = 0
+ self.root = None
+ self.output = None
+ self.test_suites = dict()
+
+ def run(self):
+ # Clean up result file names
+ results = []
+ for target in self.targets:
+ results.append(target.replace('\\', '/'))
+
+ # Dig through each result file, looking for details on pass/fail:
+ for result_file in results:
+ lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n')))
+ if len(lines) == 0:
+ raise Exception("Empty test result file: %s" % result_file)
+
+ # define an expression for your file reference
+ entry_one = Combine(
+ oneOf(list(alphas)) + ':/' +
+ Word(alphanums + '_-./'))
+
+ entry_two = Word(printables + ' ', excludeChars=':')
+ entry = entry_one | entry_two
+
+ delimiter = Literal(':').suppress()
+ # Format of a result line is `[file_name]:line:test_name:RESULT[:msg]`
+ tc_result_line = Group(ZeroOrMore(entry.setResultsName('tc_file_name'))
+ + delimiter + entry.setResultsName('tc_line_nr')
+ + delimiter + entry.setResultsName('tc_name')
+ + delimiter + entry.setResultsName('tc_status') +
+ Optional(delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line")
+
+ eol = LineEnd().suppress()
+ sol = LineStart().suppress()
+ blank_line = sol + eol
+
+ # Format of the summary line is `# Tests # Failures # Ignored`
+ tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName(
+ "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName(
+ "tc_summary")
+ tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result")
+
+ # run it and see...
+ pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line)
+ pp1.ignore(blank_line | OneOrMore("-"))
+
+ result = list()
+ for l in lines:
+ result.append((pp1.parseString(l)).asDict())
+ # delete empty results
+ result = filter(None, result)
+
+ tc_list = list()
+ for r in result:
+ if 'tc_line' in r:
+ tmp_tc_line = r['tc_line']
+
+ # get only the file name which will be used as the classname
+ if 'tc_file_name' in tmp_tc_line:
+ file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0]
+ else:
+ file_name = result_file.strip("./")
+ tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name)
+ if 'tc_status' in tmp_tc_line:
+ if str(tmp_tc_line['tc_status']) == 'IGNORE':
+ if 'tc_msg' in tmp_tc_line:
+ tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'],
+ output=r'[File]={0}, [Line]={1}'.format(
+ tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
+ else:
+ tmp_tc.add_skipped_info(message=" ")
+ elif str(tmp_tc_line['tc_status']) == 'FAIL':
+ if 'tc_msg' in tmp_tc_line:
+ tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'],
+ output=r'[File]={0}, [Line]={1}'.format(
+ tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
+ else:
+ tmp_tc.add_failure_info(message=" ")
+
+ tc_list.append((str(result_file), tmp_tc))
+
+ for k, v in tc_list:
+ try:
+ self.test_suites[k].append(v)
+ except KeyError:
+ self.test_suites[k] = [v]
+ ts = []
+ for suite_name in self.test_suites:
+ ts.append(TestSuite(suite_name, self.test_suites[suite_name]))
+
+ with open(self.output, 'w') as f:
+ TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8')
+
+ return self.report
+
+ def set_targets(self, target_array):
+ self.targets = target_array
+
+ def set_root_path(self, path):
+ self.root = path
+
+ def set_output(self, output):
+ self.output = output
+
+
+if __name__ == '__main__':
+ uts = UnityTestSummary()
+ parser = argparse.ArgumentParser(description=
+ """Takes as input the collection of *.testpass and *.testfail result
+ files, and converts them to a JUnit formatted XML.""")
+ parser.add_argument('targets_dir', metavar='result_file_directory',
+ type=str, nargs='?', default='./',
+ help="""The location of your results files.
+ Defaults to current directory if not specified.""")
+ parser.add_argument('root_path', nargs='?',
+ default='os.path.split(__file__)[0]',
+ help="""Helpful for producing more verbose output if
+ using relative paths.""")
+ parser.add_argument('--output', '-o', type=str, default="result.xml",
+ help="""The name of the JUnit-formatted file (XML).""")
+ args = parser.parse_args()
+
+ if args.targets_dir[-1] != '/':
+ args.targets_dir+='/'
+ targets = list(map(lambda x: x.replace('\\', '/'), glob(args.targets_dir + '*.test*')))
+ if len(targets) == 0:
+ raise Exception("No *.testpass or *.testfail files found in '%s'" % args.targets_dir)
+ uts.set_targets(targets)
+
+ # set the root path
+ uts.set_root_path(args.root_path)
+
+ # set output
+ uts.set_output(args.output)
+
+ # run the summarizer
+ print(uts.run())
diff --git a/deps/Unity/auto/stylize_as_junit.rb b/deps/Unity/auto/stylize_as_junit.rb
new file mode 100755
index 0000000..e4b911e
--- /dev/null
+++ b/deps/Unity/auto/stylize_as_junit.rb
@@ -0,0 +1,251 @@
+#!/usr/bin/ruby
+#
+# unity_to_junit.rb
+#
+require 'fileutils'
+require 'optparse'
+require 'ostruct'
+require 'set'
+
+require 'pp'
+
+VERSION = 1.0
+
+class ArgvParser
+ #
+ # Return a structure describing the options.
+ #
+ def self.parse(args)
+ # The options specified on the command line will be collected in *options*.
+ # We set default values here.
+ options = OpenStruct.new
+ options.results_dir = '.'
+ options.root_path = '.'
+ options.out_file = 'results.xml'
+
+ opts = OptionParser.new do |o|
+ o.banner = 'Usage: unity_to_junit.rb [options]'
+
+ o.separator ''
+ o.separator 'Specific options:'
+
+ o.on('-r', '--results <dir>', 'Look for Unity Results files here.') do |results|
+ # puts "results #{results}"
+ options.results_dir = results
+ end
+
+ o.on('-p', '--root_path <path>', 'Prepend this path to files in results.') do |root_path|
+ options.root_path = root_path
+ end
+
+ o.on('-o', '--output <filename>', 'XML file to generate.') do |out_file|
+ # puts "out_file: #{out_file}"
+ options.out_file = out_file
+ end
+
+ o.separator ''
+ o.separator 'Common options:'
+
+ # No argument, shows at tail. This will print an options summary.
+ o.on_tail('-h', '--help', 'Show this message') do
+ puts o
+ exit
+ end
+
+ # Another typical switch to print the version.
+ o.on_tail('--version', 'Show version') do
+ puts "unity_to_junit.rb version #{VERSION}"
+ exit
+ end
+ end
+
+ opts.parse!(args)
+ options
+ end
+end
+
+class UnityToJUnit
+ include FileUtils::Verbose
+ attr_reader :report, :total_tests, :failures, :ignored
+ attr_writer :targets, :root, :out_file
+
+ def initialize
+ @report = ''
+ @unit_name = ''
+ end
+
+ def run
+ # Clean up result file names
+ results = @targets.map { |target| target.tr('\\', '/') }
+ # puts "Output File: #{@out_file}"
+ f = File.new(@out_file, 'w')
+ write_xml_header(f)
+ write_suites_header(f)
+ results.each do |result_file|
+ lines = File.readlines(result_file).map(&:chomp)
+
+ raise "Empty test result file: #{result_file}" if lines.empty?
+
+ result_output = get_details(result_file, lines)
+ tests, failures, ignored = parse_test_summary(lines)
+ result_output[:counts][:total] = tests
+ result_output[:counts][:failed] = failures
+ result_output[:counts][:ignored] = ignored
+ result_output[:counts][:passed] = (result_output[:counts][:total] - result_output[:counts][:failed] - result_output[:counts][:ignored])
+
+ # use line[0] from the test output to get the test_file path and name
+ test_file_str = lines[0].tr('\\', '/')
+ test_file_str = test_file_str.split(':')
+ test_file = if test_file_str.length < 2
+ result_file
+ else
+ "#{test_file_str[0]}:#{test_file_str[1]}"
+ end
+ result_output[:source][:path] = File.dirname(test_file)
+ result_output[:source][:file] = File.basename(test_file)
+
+ # save result_output
+ @unit_name = File.basename(test_file, '.*')
+
+ write_suite_header(result_output[:counts], f)
+ write_failures(result_output, f)
+ write_tests(result_output, f)
+ write_ignored(result_output, f)
+ write_suite_footer(f)
+ end
+ write_suites_footer(f)
+ f.close
+ end
+
+ def usage(err_msg = nil)
+ puts "\nERROR: "
+ puts err_msg if err_msg
+ puts 'Usage: unity_to_junit.rb [options]'
+ puts ''
+ puts 'Specific options:'
+ puts ' -r, --results <dir> Look for Unity Results files here.'
+ puts ' -p, --root_path <path> Prepend this path to files in results.'
+ puts ' -o, --output <filename> XML file to generate.'
+ puts ''
+ puts 'Common options:'
+ puts ' -h, --help Show this message'
+ puts ' --version Show version'
+
+ exit 1
+ end
+
+ protected
+
+ def get_details(_result_file, lines)
+ results = results_structure
+ lines.each do |line|
+ line = line.tr('\\', '/')
+ _src_file, src_line, test_name, status, msg = line.split(/:/)
+ case status
+ when 'IGNORE' then results[:ignores] << { test: test_name, line: src_line, message: msg }
+ when 'FAIL' then results[:failures] << { test: test_name, line: src_line, message: msg }
+ when 'PASS' then results[:successes] << { test: test_name, line: src_line, message: msg }
+ end
+ end
+ results
+ end
+
+ def parse_test_summary(summary)
+ raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }
+
+ [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i]
+ end
+
+ private
+
+ def results_structure
+ {
+ source: { path: '', file: '' },
+ successes: [],
+ failures: [],
+ ignores: [],
+ counts: { total: 0, passed: 0, failed: 0, ignored: 0 },
+ stdout: []
+ }
+ end
+
+ def write_xml_header(stream)
+ stream.puts "<?xml version='1.0' encoding='utf-8' ?>"
+ end
+
+ def write_suites_header(stream)
+ stream.puts '<testsuites>'
+ end
+
+ def write_suite_header(counts, stream)
+ stream.puts "\t<testsuite errors=\"0\" skipped=\"#{counts[:ignored]}\" failures=\"#{counts[:failed]}\" tests=\"#{counts[:total]}\" name=\"unity\">"
+ end
+
+ def write_failures(results, stream)
+ result = results[:failures]
+ result.each do |item|
+ filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
+ stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
+ stream.puts "\t\t\t<failure message=\"#{item[:message]}\" type=\"Assertion\"/>"
+ stream.puts "\t\t\t<system-err>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</system-err>"
+ stream.puts "\t\t</testcase>"
+ end
+ end
+
+ def write_tests(results, stream)
+ result = results[:successes]
+ result.each do |item|
+ stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\" />"
+ end
+ end
+
+ def write_ignored(results, stream)
+ result = results[:ignores]
+ result.each do |item|
+ filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
+ puts "Writing ignored tests for test harness: #{filename}"
+ stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
+ stream.puts "\t\t\t<skipped message=\"#{item[:message]}\" type=\"Assertion\"/>"
+ stream.puts "\t\t\t<system-err>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</system-err>"
+ stream.puts "\t\t</testcase>"
+ end
+ end
+
+ def write_suite_footer(stream)
+ stream.puts "\t</testsuite>"
+ end
+
+ def write_suites_footer(stream)
+ stream.puts '</testsuites>'
+ end
+end
+
+if $0 == __FILE__
+ # parse out the command options
+ options = ArgvParser.parse(ARGV)
+
+ # create an instance to work with
+ utj = UnityToJUnit.new
+ begin
+ # look in the specified or current directory for result files
+ targets = "#{options.results_dir.tr('\\', '/')}**/*.test*"
+
+ results = Dir[targets]
+
+ raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?
+
+ utj.targets = results
+
+ # set the root path
+ utj.root = options.root_path
+
+ # set the output XML file name
+ # puts "Output File from options: #{options.out_file}"
+ utj.out_file = options.out_file
+
+ # run the summarizer
+ puts utj.run
+ rescue StandardError => e
+ utj.usage e.message
+ end
+end
diff --git a/deps/Unity/auto/test_file_filter.rb b/deps/Unity/auto/test_file_filter.rb
new file mode 100644
index 0000000..f4834a1
--- /dev/null
+++ b/deps/Unity/auto/test_file_filter.rb
@@ -0,0 +1,27 @@
+# ==========================================
+# 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_relative 'yaml_helper'
+
+module RakefileHelpers
+ class TestFileFilter
+ def initialize(all_files = false)
+ @all_files = all_files
+
+ return unless @all_files
+
+ file = 'test_file_filter.yml'
+ return unless File.exist?(file)
+
+ filters = YamlHelper.load_file(file)
+ @all_files = filters[:all_files]
+ @only_files = filters[:only_files]
+ @exclude_files = filters[:exclude_files]
+ end
+
+ attr_accessor :all_files, :only_files, :exclude_files
+ end
+end
diff --git a/deps/Unity/auto/type_sanitizer.rb b/deps/Unity/auto/type_sanitizer.rb
new file mode 100644
index 0000000..3d1db09
--- /dev/null
+++ b/deps/Unity/auto/type_sanitizer.rb
@@ -0,0 +1,6 @@
+module TypeSanitizer
+ def self.sanitize_c_identifier(unsanitized)
+ # convert filename to valid C identifier by replacing invalid chars with '_'
+ unsanitized.gsub(/[-\/\\.,\s]/, '_')
+ end
+end
diff --git a/deps/Unity/auto/unity_test_summary.py b/deps/Unity/auto/unity_test_summary.py
new file mode 100644
index 0000000..00c0da8
--- /dev/null
+++ b/deps/Unity/auto/unity_test_summary.py
@@ -0,0 +1,139 @@
+#! python3
+# ==========================================
+# Unity Project - A Test Framework for C
+# Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de
+# [Released under MIT License. Please refer to license.txt for details]
+# Based on the ruby script by Mike Karlesky, Mark VanderVoord, Greg Williams
+# ==========================================
+import sys
+import os
+import re
+from glob import glob
+
+class UnityTestSummary:
+ def __init__(self):
+ self.report = ''
+ self.total_tests = 0
+ self.failures = 0
+ self.ignored = 0
+
+ def run(self):
+ # Clean up result file names
+ results = []
+ for target in self.targets:
+ results.append(target.replace('\\', '/'))
+
+ # Dig through each result file, looking for details on pass/fail:
+ failure_output = []
+ ignore_output = []
+
+ for result_file in results:
+ lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n')))
+ if len(lines) == 0:
+ raise Exception("Empty test result file: %s" % result_file)
+
+ details = self.get_details(result_file, lines)
+ failures = details['failures']
+ ignores = details['ignores']
+ if len(failures) > 0: failure_output.append('\n'.join(failures))
+ if len(ignores) > 0: ignore_output.append('n'.join(ignores))
+ tests,failures,ignored = self.parse_test_summary('\n'.join(lines))
+ self.total_tests += tests
+ self.failures += failures
+ self.ignored += ignored
+
+ if self.ignored > 0:
+ self.report += "\n"
+ self.report += "--------------------------\n"
+ self.report += "UNITY IGNORED TEST SUMMARY\n"
+ self.report += "--------------------------\n"
+ self.report += "\n".join(ignore_output)
+
+ if self.failures > 0:
+ self.report += "\n"
+ self.report += "--------------------------\n"
+ self.report += "UNITY FAILED TEST SUMMARY\n"
+ self.report += "--------------------------\n"
+ self.report += '\n'.join(failure_output)
+
+ self.report += "\n"
+ self.report += "--------------------------\n"
+ self.report += "OVERALL UNITY TEST SUMMARY\n"
+ self.report += "--------------------------\n"
+ self.report += "{total_tests} TOTAL TESTS {failures} TOTAL FAILURES {ignored} IGNORED\n".format(total_tests = self.total_tests, failures=self.failures, ignored=self.ignored)
+ self.report += "\n"
+
+ return self.report
+
+ def set_targets(self, target_array):
+ self.targets = target_array
+
+ def set_root_path(self, path):
+ self.root = path
+
+ def usage(self, err_msg=None):
+ print("\nERROR: ")
+ if err_msg:
+ print(err_msg)
+ print("\nUsage: unity_test_summary.py result_file_directory/ root_path/")
+ print(" result_file_directory - The location of your results files.")
+ print(" Defaults to current directory if not specified.")
+ print(" Should end in / if specified.")
+ print(" root_path - Helpful for producing more verbose output if using relative paths.")
+ sys.exit(1)
+
+ def get_details(self, result_file, lines):
+ results = { 'failures': [], 'ignores': [], 'successes': [] }
+ for line in lines:
+ parts = line.split(':')
+ if len(parts) == 5:
+ src_file,src_line,test_name,status,msg = parts
+ elif len(parts) == 4:
+ src_file,src_line,test_name,status = parts
+ msg = ''
+ else:
+ continue
+ if len(self.root) > 0:
+ line_out = "%s%s" % (self.root, line)
+ else:
+ line_out = line
+ if status == 'IGNORE':
+ results['ignores'].append(line_out)
+ elif status == 'FAIL':
+ results['failures'].append(line_out)
+ elif status == 'PASS':
+ results['successes'].append(line_out)
+ return results
+
+ def parse_test_summary(self, summary):
+ m = re.search(r"([0-9]+) Tests ([0-9]+) Failures ([0-9]+) Ignored", summary)
+ if not m:
+ raise Exception("Couldn't parse test results: %s" % summary)
+
+ return int(m.group(1)), int(m.group(2)), int(m.group(3))
+
+
+if __name__ == '__main__':
+ uts = UnityTestSummary()
+ try:
+ #look in the specified or current directory for result files
+ if len(sys.argv) > 1:
+ targets_dir = sys.argv[1]
+ else:
+ targets_dir = './'
+ targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '**/*.test*', recursive=True)))
+ if len(targets) == 0:
+ raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir)
+ uts.set_targets(targets)
+
+ #set the root path
+ if len(sys.argv) > 2:
+ root_path = sys.argv[2]
+ else:
+ root_path = os.path.split(__file__)[0]
+ uts.set_root_path(root_path)
+
+ #run the summarizer
+ print(uts.run())
+ except Exception as e:
+ uts.usage(e)
diff --git a/deps/Unity/auto/unity_test_summary.rb b/deps/Unity/auto/unity_test_summary.rb
new file mode 100644
index 0000000..03d67a6
--- /dev/null
+++ b/deps/Unity/auto/unity_test_summary.rb
@@ -0,0 +1,139 @@
+# ==========================================
+# 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]
+# ==========================================
+
+# !/usr/bin/ruby
+#
+# unity_test_summary.rb
+#
+require 'fileutils'
+require 'set'
+
+class UnityTestSummary
+ include FileUtils::Verbose
+
+ attr_reader :report, :total_tests, :failures, :ignored
+ attr_writer :targets, :root
+
+ def initialize(_opts = {})
+ @report = ''
+ @total_tests = 0
+ @failures = 0
+ @ignored = 0
+ end
+
+ def run
+ # Clean up result file names
+ results = @targets.map { |target| target.tr('\\', '/') }
+
+ # Dig through each result file, looking for details on pass/fail:
+ failure_output = []
+ ignore_output = []
+
+ results.each do |result_file|
+ lines = File.readlines(result_file).map(&:chomp)
+
+ raise "Empty test result file: #{result_file}" if lines.empty?
+
+ output = get_details(result_file, lines)
+ failure_output << output[:failures] unless output[:failures].empty?
+ ignore_output << output[:ignores] unless output[:ignores].empty?
+ tests, failures, ignored = parse_test_summary(lines)
+ @total_tests += tests
+ @failures += failures
+ @ignored += ignored
+ end
+
+ if @ignored > 0
+ @report += "\n"
+ @report += "--------------------------\n"
+ @report += "UNITY IGNORED TEST SUMMARY\n"
+ @report += "--------------------------\n"
+ @report += ignore_output.flatten.join("\n")
+ end
+
+ if @failures > 0
+ @report += "\n"
+ @report += "--------------------------\n"
+ @report += "UNITY FAILED TEST SUMMARY\n"
+ @report += "--------------------------\n"
+ @report += failure_output.flatten.join("\n")
+ end
+
+ @report += "\n"
+ @report += "--------------------------\n"
+ @report += "OVERALL UNITY TEST SUMMARY\n"
+ @report += "--------------------------\n"
+ @report += "#{@total_tests} TOTAL TESTS #{@failures} TOTAL FAILURES #{@ignored} IGNORED\n"
+ @report += "\n"
+ end
+
+ def usage(err_msg = nil)
+ puts "\nERROR: "
+ puts err_msg if err_msg
+ puts "\nUsage: unity_test_summary.rb result_file_directory/ root_path/"
+ puts ' result_file_directory - The location of your results files.'
+ puts ' Defaults to current directory if not specified.'
+ puts ' Should end in / if specified.'
+ puts ' root_path - Helpful for producing more verbose output if using relative paths.'
+ exit 1
+ end
+
+ protected
+
+ def get_details(_result_file, lines)
+ results = { failures: [], ignores: [], successes: [] }
+ lines.each do |line|
+ status_match = line.match(/^[^:]+:[^:]+:\w+(?:\([^)]*\))?:([^:]+):?/)
+ next unless status_match
+
+ status = status_match.captures[0]
+
+ line_out = (@root && (@root != 0) ? "#{@root}#{line}" : line).gsub(/\//, '\\')
+ case status
+ when 'IGNORE' then results[:ignores] << line_out
+ when 'FAIL' then results[:failures] << line_out
+ when 'PASS' then results[:successes] << line_out
+ end
+ end
+ results
+ end
+
+ def parse_test_summary(summary)
+ raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }
+
+ [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i]
+ end
+end
+
+if $0 == __FILE__
+
+ # parse out the command options
+ opts, args = ARGV.partition { |v| v =~ /^--\w+/ }
+ opts.map! { |v| v[2..].to_sym }
+
+ # create an instance to work with
+ uts = UnityTestSummary.new(opts)
+
+ begin
+ # look in the specified or current directory for result files
+ args[0] ||= './'
+ targets = "#{ARGV[0].tr('\\', '/')}**/*.test*"
+ results = Dir[targets]
+
+ raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?
+
+ uts.targets = results
+
+ # set the root path
+ args[1] ||= "#{Dir.pwd}/"
+ uts.root = ARGV[1]
+
+ # run the summarizer
+ puts uts.run
+ rescue StandardError => e
+ uts.usage e.message
+ end
+end
diff --git a/deps/Unity/auto/yaml_helper.rb b/deps/Unity/auto/yaml_helper.rb
new file mode 100644
index 0000000..3296ba0
--- /dev/null
+++ b/deps/Unity/auto/yaml_helper.rb
@@ -0,0 +1,22 @@
+# ==========================================
+# 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'
+
+module YamlHelper
+ def self.load(body)
+ if YAML.respond_to?(:unsafe_load)
+ YAML.unsafe_load(body)
+ else
+ YAML.load(body)
+ end
+ end
+
+ def self.load_file(file)
+ body = File.read(file)
+ self.load(body)
+ end
+end