mirror of
https://github.com/DaveGamble/cJSON.git
synced 2023-08-10 21:13:26 +03:00
Squashed 'tests/unity/' changes from 1f52255..f96c055
f96c055 this is a minor release 2c7629a Documentation Updates b8bfb01 Add support for AStyle in test makefile. It’s going to assume you have it installed. e36d8b5 Merge pull request #276 from wolf99/pdf-to-markdown 1e43967 Add EACH_EQUAL changes e2cc679 Add newlines after headings for best practice, trim trailing spaces & convert sneaky incorrectly coded chars 192d517 Remove PDFs c48f6c9 Add Github Markdown versions of documents 2a5b24f Finished updating all Ruby scripts to match our coding standard. Woo! 3e0a712 Started to flesh out rubocop settings for this project. Added rakefile tasks to do so. Updated first script to make it compliant. 23f9c16 Another round of fixing things that the stylizer “corrected” for me. 3a6cca3 Fixed things that the stylizer “autocorrected” to wrong. ;) 3062c39 Starting to enforce our coding style. The first step is that we’ve pulled in Rubocop to check out Ruby syntax. There is likely a bit of customization to do yet AND there is definitely that backlog of todo’s that we just told it to ignore. 550d58b Attempt to fix remaining issues with casting ee038c2 Ha! Forgot to add the correct comment style d6b3508 Clean up some const issues, particularly when moving between single and double pointers 4ffafce Finish updating documentation to match 083564b Update docs to also understand the new Each Equal handlers 0dddf49 also update strings to support each element of an array. a11a137 Added memory each equal assertion d8d67a7 Added each_equal assertions for float and double b7956ea Added more tests for all the numerical types when performing each_equal assertions 7fe3191 Added some tests to prove this works. Still work in progress 56eeacd prepare for comparing value to array by setting up explicit compare of array to array in ints 7b80885 Merge pull request #272 from FSMaxB/gcc43-wconversion 0781e74 Add our coding standard to documentation c3658a0 Dropped support for pre-2.0 versions of Ruby (not even rubylang supports them anymore) 8a45ccf Use custom mock prefix when searching for mock header files. #263 689610b reorder includes in generated test runners 43c7511 stdlib.h explicitly called in fixtures when malloc used, now. (Fixes issue #268) 1c556d2 Fix -Wconversion with gcc-4.3 8723d50 Turn UNITY_OUTPUT_FLUSH off by default. Added a quick-define for the most common case: UNITY_USE_FLUSH_STDOUT. Clarified documentation. Fixes issue #269 c67a4ff - Add ability to detect TEST_FILE(“filename.c”) specifications in test files 41ee499 Tiny tweaks to make Unity fit in more smoothly with Ceedling git-subtree-dir: tests/unity git-subtree-split: f96c05532b3e00c9ca77e58fc07f9401cd46510d
This commit is contained in:
@ -4,63 +4,62 @@
|
||||
# [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
|
||||
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
|
||||
# puts 'Win32API Library Found.'
|
||||
# puts
|
||||
end
|
||||
|
||||
class ColourCommandLine
|
||||
def initialize
|
||||
if RUBY_PLATFORM =~/(win|w)32$/
|
||||
get_std_handle = Win32API.new("kernel32", "GetStdHandle", ['L'], 'L')
|
||||
@set_console_txt_attrb =
|
||||
Win32API.new("kernel32","SetConsoleTextAttribute",['L','N'], 'I')
|
||||
@hout = get_std_handle.call(-11)
|
||||
end
|
||||
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,self.win32_colour(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
|
||||
"\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
|
||||
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)
|
||||
def posix_colour(colour)
|
||||
# ANSI Escape Codes - Foreground colors
|
||||
# | Code | Color |
|
||||
# | 39 | Default foreground color |
|
||||
@ -81,35 +80,39 @@ class ColourCommandLine
|
||||
# | 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
|
||||
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
|
||||
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 # ColourCommandLine
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -2,38 +2,38 @@
|
||||
# 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 "#{File.expand_path(File.dirname(__FILE__))}/colour_prompt"
|
||||
|
||||
$colour_output = true
|
||||
|
||||
def report(message)
|
||||
if not $colour_output
|
||||
if !$colour_output
|
||||
$stdout.puts(message)
|
||||
else
|
||||
message = message.join('\n') if (message.class == Array)
|
||||
message = message.join('\n') if message.class == 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
|
||||
($1.to_i == 0) ? :green : :red
|
||||
when /PASS/
|
||||
:green
|
||||
when /^OK$/
|
||||
:green
|
||||
when /(?:FAIL|ERROR)/
|
||||
:red
|
||||
when /IGNORE/
|
||||
:yellow
|
||||
when /^(?:Creating|Compiling|Linking)/
|
||||
:white
|
||||
else
|
||||
:silver
|
||||
end
|
||||
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
|
||||
end
|
||||
|
@ -12,8 +12,8 @@ require 'rubygems'
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
|
||||
#TEMPLATE_TST
|
||||
TEMPLATE_TST ||= %q[#include "unity.h"
|
||||
# TEMPLATE_TST
|
||||
TEMPLATE_TST ||= '#include "unity.h"
|
||||
%2$s#include "%1$s.h"
|
||||
|
||||
void setUp(void)
|
||||
@ -28,115 +28,118 @@ void test_%1$s_NeedToImplement(void)
|
||||
{
|
||||
TEST_IGNORE_MESSAGE("Need to Implement %1$s");
|
||||
}
|
||||
]
|
||||
'.freeze
|
||||
|
||||
#TEMPLATE_SRC
|
||||
TEMPLATE_SRC ||= %q[%2$s#include "%1$s.h"
|
||||
]
|
||||
# TEMPLATE_SRC
|
||||
TEMPLATE_SRC ||= '%2$s#include "%1$s.h"
|
||||
'.freeze
|
||||
|
||||
#TEMPLATE_INC
|
||||
TEMPLATE_INC ||= %q[#ifndef _%3$s_H
|
||||
# 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)
|
||||
|
||||
def initialize(options = nil)
|
||||
here = File.expand_path(File.dirname(__FILE__)) + '/'
|
||||
|
||||
@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"
|
||||
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] = here + "../src/" if @options[:path_src].nil?
|
||||
@options[:path_src] = here + '../src/' if @options[:path_src].nil?
|
||||
@options[:path_inc] = @options[:path_src] if @options[:path_inc].nil?
|
||||
@options[:path_tst] = here + "../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)
|
||||
@options[:path_tst] = here + '../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 => [] }
|
||||
}
|
||||
}
|
||||
# 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 => [],
|
||||
pattern: 'src',
|
||||
includes: {
|
||||
src: [],
|
||||
inc: [],
|
||||
tst: []
|
||||
},
|
||||
:update_svn => false,
|
||||
:boilerplates => {},
|
||||
:test_prefix => 'Test',
|
||||
:mock_prefix => 'Mock',
|
||||
update_svn: false,
|
||||
boilerplates: {},
|
||||
test_prefix: 'Test',
|
||||
mock_prefix: 'Mock'
|
||||
}
|
||||
end
|
||||
|
||||
############################
|
||||
def self.grab_config(config_file)
|
||||
options = self.default_options
|
||||
unless (config_file.nil? or config_file.empty?)
|
||||
options = default_options
|
||||
unless config_file.nil? || config_file.empty?
|
||||
require 'yaml'
|
||||
yaml_guts = YAML.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
|
||||
return(options)
|
||||
options
|
||||
end
|
||||
|
||||
############################
|
||||
def files_to_operate_on(module_name, pattern=nil)
|
||||
#strip any leading path information from the module name and save for later
|
||||
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
|
||||
# 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] },
|
||||
]
|
||||
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] }]
|
||||
|
||||
#prepare the pattern for use
|
||||
# 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
|
||||
if (pattern == 'test')
|
||||
triad.reject!{|v| v[:inc] != :tst }
|
||||
end
|
||||
# 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 = []
|
||||
@ -145,26 +148,26 @@ class UnityModuleGenerator
|
||||
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],
|
||||
:boilerplate => cfg[:boilerplate],
|
||||
:includes => case(cfg[:inc])
|
||||
when :src then (@options[:includes][:src] || []) | pattern_traits[:inc].map{|f| f % [module_name]}
|
||||
when :inc then (@options[:includes][:inc] || [])
|
||||
when :tst then (@options[:includes][:tst] || []) | pattern_traits[:inc].map{|f| "#{@options[:mock_prefix]}#{f}" % [module_name]}
|
||||
end
|
||||
path: (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath,
|
||||
name: submodule_name,
|
||||
template: cfg[:template],
|
||||
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
|
||||
|
||||
return files
|
||||
files
|
||||
end
|
||||
|
||||
############################
|
||||
def create_filename(part1, part2="")
|
||||
def create_filename(part1, part2 = '')
|
||||
if part2.empty?
|
||||
case(@options[:naming])
|
||||
case (@options[:naming])
|
||||
when 'bumpy' then part1
|
||||
when 'camel' then part1
|
||||
when 'snake' then part1.downcase
|
||||
@ -172,49 +175,45 @@ class UnityModuleGenerator
|
||||
else part1.downcase
|
||||
end
|
||||
else
|
||||
case(@options[:naming])
|
||||
case (@options[:naming])
|
||||
when 'bumpy' then part1 + part2
|
||||
when 'camel' then part1 + part2
|
||||
when 'snake' then part1.downcase + "_" + part2.downcase
|
||||
when 'caps' then part1.upcase + "_" + part2.upcase
|
||||
else part1.downcase + "_" + part2.downcase
|
||||
when 'snake' then part1.downcase + '_' + part2.downcase
|
||||
when 'caps' then part1.upcase + '_' + part2.upcase
|
||||
else part1.downcase + '_' + part2.downcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
############################
|
||||
def generate(module_name, pattern=nil)
|
||||
|
||||
def generate(module_name, pattern = nil)
|
||||
files = files_to_operate_on(module_name, pattern)
|
||||
|
||||
#Abort if all of the module files already exist
|
||||
# Abort if all of the module files already exist
|
||||
all_files_exist = true
|
||||
files.each do |file|
|
||||
if not File.exist?(file[:path])
|
||||
all_files_exist = false
|
||||
end
|
||||
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|
|
||||
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)
|
||||
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{|f| "#include \"#{f}\"\n"}.join,
|
||||
file[:name].upcase ]
|
||||
)
|
||||
f.write(file[:template] % [file[:name],
|
||||
file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join,
|
||||
file[:name].upcase])
|
||||
end
|
||||
if (@options[:update_svn])
|
||||
if @options[:update_svn]
|
||||
`svn add \"#{file[:path]}\"`
|
||||
if $?.exitstatus == 0
|
||||
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!"
|
||||
@ -227,8 +226,7 @@ class UnityModuleGenerator
|
||||
end
|
||||
|
||||
############################
|
||||
def destroy(module_name, pattern=nil)
|
||||
|
||||
def destroy(module_name, pattern = nil)
|
||||
files_to_operate_on(module_name, pattern).each do |filespec|
|
||||
file = filespec[:path]
|
||||
if File.exist?(file)
|
||||
@ -243,66 +241,65 @@ class UnityModuleGenerator
|
||||
puts "File #{file} does not exist so cannot be removed."
|
||||
end
|
||||
end
|
||||
puts "Destroy Complete"
|
||||
puts 'Destroy Complete'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
############################
|
||||
#Handle As Command Line If Called That Way
|
||||
if ($0 == __FILE__)
|
||||
# Handle As Command Line If Called That Way
|
||||
if $0 == __FILE__
|
||||
destroy = false
|
||||
options = { }
|
||||
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] = $1
|
||||
when /^-s\"?(.+)\"?/ then options[:path_src] = $1
|
||||
when /^-i\"?(.+)\"?/ then options[:path_inc] = $1
|
||||
when /^-t\"?(.+)\"?/ then options[:path_tst] = $1
|
||||
when /^-n\"?(.+)\"?/ then options[:naming] = $1
|
||||
when /^-y\"?(.+)\"?/ then options = UnityModuleGenerator.grab_config($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 = []
|
||||
else
|
||||
raise "ERROR: Unknown option specified '#{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
|
||||
|
||||
if (!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. (DEFAULT)",
|
||||
" 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")
|
||||
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. (DEFAULT)',
|
||||
' 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)
|
||||
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)
|
||||
|
@ -4,75 +4,70 @@
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
$QUICK_RUBY_VERSION = RUBY_VERSION.split('.').inject(0){|vv,v| vv * 100 + v.to_i }
|
||||
File.expand_path(File.join(File.dirname(__FILE__),'colour_prompt'))
|
||||
File.expand_path(File.join(File.dirname(__FILE__), 'colour_prompt'))
|
||||
|
||||
class UnityTestRunnerGenerator
|
||||
|
||||
def initialize(options = nil)
|
||||
@options = UnityTestRunnerGenerator.default_options
|
||||
case(options)
|
||||
when NilClass then @options
|
||||
when String then @options.merge!(UnityTestRunnerGenerator.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"
|
||||
case options
|
||||
when NilClass then @options
|
||||
when String then @options.merge!(UnityTestRunnerGenerator.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
|
||||
require "#{File.expand_path(File.dirname(__FILE__))}/type_sanitizer"
|
||||
end
|
||||
|
||||
def self.default_options
|
||||
{
|
||||
:includes => [],
|
||||
:defines => [],
|
||||
:plugins => [],
|
||||
:framework => :unity,
|
||||
:test_prefix => "test|spec|should",
|
||||
:setup_name => "setUp",
|
||||
:teardown_name => "tearDown",
|
||||
:main_name => "main", #set to :auto to automatically generate each time
|
||||
:main_export_decl => "",
|
||||
:cmdline_args => false,
|
||||
:use_param_tests => false,
|
||||
includes: [],
|
||||
defines: [],
|
||||
plugins: [],
|
||||
framework: :unity,
|
||||
test_prefix: 'test|spec|should',
|
||||
mock_prefix: 'Mock',
|
||||
setup_name: 'setUp',
|
||||
teardown_name: 'tearDown',
|
||||
main_name: 'main', # set to :auto to automatically generate each time
|
||||
main_export_decl: '',
|
||||
cmdline_args: false,
|
||||
use_param_tests: false
|
||||
}
|
||||
end
|
||||
|
||||
def self.grab_config(config_file)
|
||||
options = self.default_options
|
||||
unless (config_file.nil? or config_file.empty?)
|
||||
options = default_options
|
||||
unless config_file.nil? || config_file.empty?
|
||||
require 'yaml'
|
||||
yaml_guts = YAML.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
|
||||
return(options)
|
||||
options
|
||||
end
|
||||
|
||||
def run(input_file, output_file, options=nil)
|
||||
tests = []
|
||||
testfile_includes = []
|
||||
used_mocks = []
|
||||
|
||||
def run(input_file, output_file, options = nil)
|
||||
@options.merge!(options) unless options.nil?
|
||||
module_name = File.basename(input_file)
|
||||
|
||||
#pull required data from source file
|
||||
# pull required data from source file
|
||||
source = File.read(input_file)
|
||||
source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil) if ($QUICK_RUBY_VERSION > 10900)
|
||||
source = source.force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
|
||||
tests = find_tests(source)
|
||||
headers = find_includes(source)
|
||||
testfile_includes = (headers[:local] + headers[:system])
|
||||
used_mocks = find_mocks(testfile_includes)
|
||||
testfile_includes = (testfile_includes - used_mocks)
|
||||
testfile_includes.delete_if{|inc| inc =~ /(unity|cmock)/}
|
||||
testfile_includes.delete_if { |inc| inc =~ /(unity|cmock)/ }
|
||||
|
||||
#build runner file
|
||||
# build runner file
|
||||
generate(input_file, output_file, tests, used_mocks, testfile_includes)
|
||||
|
||||
#determine which files were used to return them
|
||||
# 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 += testfile_includes.map { |filename| filename + '.c' } unless testfile_includes.empty?
|
||||
all_files_used += @options[:includes] unless @options[:includes].empty?
|
||||
return all_files_used.uniq
|
||||
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)
|
||||
@ -80,15 +75,16 @@ class UnityTestRunnerGenerator
|
||||
create_header(output, used_mocks, testfile_includes)
|
||||
create_externs(output, tests, used_mocks)
|
||||
create_mock_management(output, used_mocks)
|
||||
create_suite_setup_and_teardown(output)
|
||||
create_suite_setup(output)
|
||||
create_suite_teardown(output)
|
||||
create_reset(output, used_mocks)
|
||||
create_main(output, input_file, tests, used_mocks)
|
||||
end
|
||||
|
||||
if (@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
|
||||
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
|
||||
|
||||
@ -96,103 +92,102 @@ class UnityTestRunnerGenerator
|
||||
tests_and_line_numbers = []
|
||||
|
||||
source_scrubbed = source.clone
|
||||
source_scrubbed = source_scrubbed.gsub(/"[^"\n]*"/, '') # remove things in strings
|
||||
source_scrubbed = source_scrubbed.gsub(/"[^"\n]*"/, '') # remove things in strings
|
||||
source_scrubbed = source_scrubbed.gsub(/\/\/.*$/, '') # remove line comments
|
||||
source_scrubbed = source_scrubbed.gsub(/\/\*.*?\*\//m, '') # remove block comments
|
||||
lines = source_scrubbed.split(/(^\s*\#.*$) # Treat preprocessor directives as a logical line
|
||||
| (;|\{|\}) /x) # Match ;, {, and } as end of lines
|
||||
|
||||
lines.each_with_index do |line, index|
|
||||
#find tests
|
||||
if line =~ /^((?:\s*TEST_CASE\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/
|
||||
arguments = $1
|
||||
name = $2
|
||||
call = $3
|
||||
params = $4
|
||||
args = nil
|
||||
if (@options[:use_param_tests] and !arguments.empty?)
|
||||
args = []
|
||||
arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) {|a| args << a[0]}
|
||||
end
|
||||
tests_and_line_numbers << { :test => name, :args => args, :call => call, :params => params, :line_number => 0 }
|
||||
lines.each_with_index do |line, _index|
|
||||
# find tests
|
||||
next unless line =~ /^((?:\s*TEST_CASE\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/
|
||||
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 = []
|
||||
arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) { |a| args << a[0] }
|
||||
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] }
|
||||
tests_and_line_numbers.uniq! { |v| v[:test] }
|
||||
|
||||
#determine line numbers and create tests to run
|
||||
# determine line numbers and create tests to run
|
||||
source_lines = source.split("\n")
|
||||
source_index = 0;
|
||||
source_index = 0
|
||||
tests_and_line_numbers.size.times do |i|
|
||||
source_lines[source_index..-1].each_with_index do |line, index|
|
||||
if (line =~ /#{tests_and_line_numbers[i][:test]}/)
|
||||
source_index += index
|
||||
tests_and_line_numbers[i][:line_number] = source_index + 1
|
||||
break
|
||||
end
|
||||
next unless line =~ /#{tests_and_line_numbers[i][:test]}/
|
||||
source_index += index
|
||||
tests_and_line_numbers[i][:line_number] = source_index + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return tests_and_line_numbers
|
||||
tests_and_line_numbers
|
||||
end
|
||||
|
||||
def find_includes(source)
|
||||
|
||||
#remove comments (block and line, in three steps to ensure correct precedence)
|
||||
# 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
|
||||
# parse out includes
|
||||
includes = {
|
||||
:local => source.scan(/^\s*#include\s+\"\s*(.+)\.[hH]\s*\"/).flatten,
|
||||
:system => source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }
|
||||
local: source.scan(/^\s*#include\s+\"\s*(.+)\.[hH]\s*\"/).flatten,
|
||||
system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" },
|
||||
linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+)\.[cC]\w*\s*\"/).flatten
|
||||
}
|
||||
return includes
|
||||
includes
|
||||
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 =~ /^mock/i)
|
||||
mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}/i
|
||||
end
|
||||
return mock_headers
|
||||
mock_headers
|
||||
end
|
||||
|
||||
def create_header(output, mocks, testfile_includes=[])
|
||||
def create_header(output, mocks, testfile_includes = [])
|
||||
output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */')
|
||||
create_runtest(output, mocks)
|
||||
output.puts("\n/*=======Automagically Detected Files To Include=====*/")
|
||||
output.puts("#include \"#{@options[:framework].to_s}.h\"")
|
||||
output.puts('#include "cmock.h"') unless (mocks.empty?)
|
||||
output.puts("#include \"#{@options[:framework]}.h\"")
|
||||
output.puts('#include "cmock.h"') unless mocks.empty?
|
||||
output.puts('#include <setjmp.h>')
|
||||
output.puts('#include <stdio.h>')
|
||||
output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception)
|
||||
if (@options[:defines] && !@options[:defines].empty?)
|
||||
@options[:defines].each {|d| output.puts("#define #{d}")}
|
||||
if @options[:defines] && !@options[:defines].empty?
|
||||
@options[:defines].each { |d| output.puts("#define #{d}") }
|
||||
end
|
||||
if (@options[:header_file] && !@options[:header_file].empty?)
|
||||
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.gsub('.h','')}.h\""}")
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}")
|
||||
end
|
||||
testfile_includes.each do |inc|
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h','')}.h\""}")
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}")
|
||||
end
|
||||
end
|
||||
mocks.each do |mock|
|
||||
output.puts("#include \"#{mock.gsub('.h','')}.h\"")
|
||||
end
|
||||
if @options[:enforce_strict_ordering]
|
||||
output.puts('')
|
||||
output.puts('int GlobalExpectCount;')
|
||||
output.puts('int GlobalVerifyOrder;')
|
||||
output.puts('char* GlobalOrderError;')
|
||||
output.puts("#include \"#{mock.gsub('.h', '')}.h\"")
|
||||
end
|
||||
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)
|
||||
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);")
|
||||
@ -203,55 +198,60 @@ class UnityTestRunnerGenerator
|
||||
end
|
||||
|
||||
def create_mock_management(output, mock_headers)
|
||||
unless (mock_headers.empty?)
|
||||
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")
|
||||
return if mock_headers.empty?
|
||||
|
||||
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("\n/*=======Mock Management=====*/")
|
||||
output.puts('static void CMock_Init(void)')
|
||||
output.puts('{')
|
||||
|
||||
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")
|
||||
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_suite_setup_and_teardown(output)
|
||||
unless (@options[:suite_setup].nil?)
|
||||
output.puts("\n/*=======Suite Setup=====*/")
|
||||
output.puts("static void suite_setup(void)")
|
||||
output.puts("{")
|
||||
output.puts(@options[:suite_setup])
|
||||
output.puts("}")
|
||||
end
|
||||
unless (@options[:suite_teardown].nil?)
|
||||
output.puts("\n/*=======Suite Teardown=====*/")
|
||||
output.puts("static int suite_teardown(int num_failures)")
|
||||
output.puts("{")
|
||||
output.puts(@options[:suite_teardown])
|
||||
output.puts("}")
|
||||
end
|
||||
def create_suite_setup(output)
|
||||
return if @options[:suite_setup].nil?
|
||||
|
||||
output.puts("\n/*=======Suite Setup=====*/")
|
||||
output.puts('static void suite_setup(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('static int suite_teardown(int num_failures)')
|
||||
output.puts('{')
|
||||
output.puts(@options[:suite_teardown])
|
||||
output.puts('}')
|
||||
end
|
||||
|
||||
def create_runtest(output, used_mocks)
|
||||
@ -259,124 +259,124 @@ class UnityTestRunnerGenerator
|
||||
va_args1 = @options[:use_param_tests] ? ', ...' : ''
|
||||
va_args2 = @options[:use_param_tests] ? '__VA_ARGS__' : ''
|
||||
output.puts("\n/*=======Test Runner Used To Run Each Test Below=====*/")
|
||||
output.puts("#define RUN_TEST_NO_ARGS") if @options[:use_param_tests]
|
||||
output.puts('#define RUN_TEST_NO_ARGS') if @options[:use_param_tests]
|
||||
output.puts("#define RUN_TEST(TestFunc, TestLineNum#{va_args1}) \\")
|
||||
output.puts("{ \\")
|
||||
output.puts('{ \\')
|
||||
output.puts(" Unity.CurrentTestName = #TestFunc#{va_args2.empty? ? '' : " \"(\" ##{va_args2} \")\""}; \\")
|
||||
output.puts(" Unity.CurrentTestLineNumber = TestLineNum; \\")
|
||||
output.puts(" if (UnityTestMatches()) { \\") if (@options[:cmdline_args])
|
||||
output.puts(" Unity.NumberOfTests++; \\")
|
||||
output.puts(" CMock_Init(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" UNITY_CLR_DETAILS(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" if (TEST_PROTECT()) \\")
|
||||
output.puts(" { \\")
|
||||
output.puts(" CEXCEPTION_T e; \\") if cexception
|
||||
output.puts(" Try { \\") if cexception
|
||||
output.puts(' Unity.CurrentTestLineNumber = TestLineNum; \\')
|
||||
output.puts(' if (UnityTestMatches()) { \\') if @options[:cmdline_args]
|
||||
output.puts(' Unity.NumberOfTests++; \\')
|
||||
output.puts(' CMock_Init(); \\') unless used_mocks.empty?
|
||||
output.puts(' UNITY_CLR_DETAILS(); \\') unless used_mocks.empty?
|
||||
output.puts(' if (TEST_PROTECT()) \\')
|
||||
output.puts(' { \\')
|
||||
output.puts(' CEXCEPTION_T e; \\') if cexception
|
||||
output.puts(' Try { \\') if cexception
|
||||
output.puts(" #{@options[:setup_name]}(); \\")
|
||||
output.puts(" TestFunc(#{va_args2}); \\")
|
||||
output.puts(" } Catch(e) { TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, \"Unhandled Exception!\"); } \\") if cexception
|
||||
output.puts(" } \\")
|
||||
output.puts(" if (TEST_PROTECT()) \\")
|
||||
output.puts(" { \\")
|
||||
output.puts(' } Catch(e) { TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!"); } \\') if cexception
|
||||
output.puts(' } \\')
|
||||
output.puts(' if (TEST_PROTECT()) \\')
|
||||
output.puts(' { \\')
|
||||
output.puts(" #{@options[:teardown_name]}(); \\")
|
||||
output.puts(" CMock_Verify(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" } \\")
|
||||
output.puts(" CMock_Destroy(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" UnityConcludeTest(); \\")
|
||||
output.puts(" } \\") if (@options[:cmdline_args])
|
||||
output.puts(' CMock_Verify(); \\') unless used_mocks.empty?
|
||||
output.puts(' } \\')
|
||||
output.puts(' CMock_Destroy(); \\') unless used_mocks.empty?
|
||||
output.puts(' UnityConcludeTest(); \\')
|
||||
output.puts(' } \\') if @options[:cmdline_args]
|
||||
output.puts("}\n")
|
||||
end
|
||||
|
||||
def create_reset(output, used_mocks)
|
||||
output.puts("\n/*=======Test Reset Option=====*/")
|
||||
output.puts("void resetTest(void);")
|
||||
output.puts("void resetTest(void)")
|
||||
output.puts("{")
|
||||
output.puts(" CMock_Verify();") unless (used_mocks.empty?)
|
||||
output.puts(" CMock_Destroy();") unless (used_mocks.empty?)
|
||||
output.puts('void resetTest(void);')
|
||||
output.puts('void resetTest(void)')
|
||||
output.puts('{')
|
||||
output.puts(' CMock_Verify();') unless used_mocks.empty?
|
||||
output.puts(' CMock_Destroy();') unless used_mocks.empty?
|
||||
output.puts(" #{@options[:teardown_name]}();")
|
||||
output.puts(" CMock_Init();") unless (used_mocks.empty?)
|
||||
output.puts(' CMock_Init();') unless used_mocks.empty?
|
||||
output.puts(" #{@options[:setup_name]}();")
|
||||
output.puts("}")
|
||||
output.puts('}')
|
||||
end
|
||||
|
||||
def create_main(output, filename, tests, used_mocks)
|
||||
output.puts("\n\n/*=======MAIN=====*/")
|
||||
main_name = (@options[:main_name].to_sym == :auto) ? "main_#{filename.gsub('.c','')}" : "#{@options[:main_name]}"
|
||||
if (@options[:cmdline_args])
|
||||
if (main_name != "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','')}.\");")
|
||||
output.puts(" UNITY_PRINT_EOL();")
|
||||
if (@options[:use_param_tests])
|
||||
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', '')}.\");")
|
||||
output.puts(' UNITY_PRINT_EOL();')
|
||||
if @options[:use_param_tests]
|
||||
tests.each do |test|
|
||||
if ((test[:args].nil?) or (test[:args].empty?))
|
||||
if test[:args].nil? || test[:args].empty?
|
||||
output.puts(" UnityPrint(\" #{test[:test]}(RUN_TEST_NO_ARGS)\");")
|
||||
output.puts(" UNITY_PRINT_EOL();")
|
||||
output.puts(' UNITY_PRINT_EOL();')
|
||||
else
|
||||
test[:args].each do |args|
|
||||
output.puts(" UnityPrint(\" #{test[:test]}(#{args})\");")
|
||||
output.puts(" UNITY_PRINT_EOL();")
|
||||
output.puts(' UNITY_PRINT_EOL();')
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
tests.each { |test| output.puts(" UnityPrint(\" #{test[:test]}\");\n UNITY_PRINT_EOL();")}
|
||||
tests.each { |test| output.puts(" UnityPrint(\" #{test[:test]}\");\n UNITY_PRINT_EOL();") }
|
||||
end
|
||||
output.puts(" return 0;")
|
||||
output.puts(" }")
|
||||
output.puts(" return parse_status;")
|
||||
output.puts(" }")
|
||||
output.puts(' return 0;')
|
||||
output.puts(' }')
|
||||
output.puts(' return parse_status;')
|
||||
output.puts(' }')
|
||||
else
|
||||
if (main_name != "main")
|
||||
if main_name != 'main'
|
||||
output.puts("#{@options[:main_export_decl]} int #{main_name}(void);")
|
||||
end
|
||||
output.puts("int #{main_name}(void)")
|
||||
output.puts("{")
|
||||
output.puts('{')
|
||||
end
|
||||
output.puts(" suite_setup();") unless @options[:suite_setup].nil?
|
||||
output.puts(" UnityBegin(\"#{filename.gsub(/\\/,'\\\\\\')}\");")
|
||||
if (@options[:use_param_tests])
|
||||
output.puts(' suite_setup();') unless @options[:suite_setup].nil?
|
||||
output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");")
|
||||
if @options[:use_param_tests]
|
||||
tests.each do |test|
|
||||
if ((test[:args].nil?) or (test[:args].empty?))
|
||||
if test[:args].nil? || test[:args].empty?
|
||||
output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]}, RUN_TEST_NO_ARGS);")
|
||||
else
|
||||
test[:args].each {|args| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]}, #{args});")}
|
||||
test[:args].each { |args| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]}, #{args});") }
|
||||
end
|
||||
end
|
||||
else
|
||||
tests.each { |test| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]});") }
|
||||
tests.each { |test| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]});") }
|
||||
end
|
||||
output.puts()
|
||||
output.puts(" CMock_Guts_MemFreeFinal();") unless used_mocks.empty?
|
||||
output.puts(" return #{@options[:suite_teardown].nil? ? "" : "suite_teardown"}(UnityEnd());")
|
||||
output.puts("}")
|
||||
output.puts
|
||||
output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty?
|
||||
output.puts(" return #{@options[:suite_teardown].nil? ? '' : 'suite_teardown'}(UnityEnd());")
|
||||
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. */")
|
||||
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].to_s}.h\"")
|
||||
output.puts('#include "cmock.h"') unless (used_mocks.empty?)
|
||||
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.gsub('.h','')}.h\""}")
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}")
|
||||
end
|
||||
testfile_includes.each do |inc|
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h','')}.h\""}")
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}")
|
||||
end
|
||||
output.puts "\n"
|
||||
tests.each do |test|
|
||||
if ((test[:params].nil?) or (test[:params].empty?))
|
||||
if test[:params].nil? || test[:params].empty?
|
||||
output.puts("void #{test[:test]}(void);")
|
||||
else
|
||||
output.puts("void #{test[:test]}(#{test[:params]});")
|
||||
@ -386,50 +386,52 @@ class UnityTestRunnerGenerator
|
||||
end
|
||||
end
|
||||
|
||||
if ($0 == __FILE__)
|
||||
options = { :includes => [] }
|
||||
yaml_file = nil
|
||||
if $0 == __FILE__
|
||||
options = { includes: [] }
|
||||
|
||||
#parse out all the options first (these will all be removed as we go)
|
||||
# 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 /\.*\.ya?ml/
|
||||
options = UnityTestRunnerGenerator.grab_config(arg); true
|
||||
when /--(\w+)=\"?(.*)\"?/
|
||||
options[$1.to_sym] = $2; true
|
||||
when /\.*\.h/
|
||||
options[:includes] << arg; true
|
||||
else false
|
||||
case arg
|
||||
when '-cexception'
|
||||
options[:plugins] = [:cexception]
|
||||
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 /\.*\.h/
|
||||
options[:includes] << arg
|
||||
true
|
||||
else false
|
||||
end
|
||||
end
|
||||
|
||||
#make sure there is at least one parameter left (the input file)
|
||||
if !ARGV[0]
|
||||
# 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",
|
||||
" --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",
|
||||
" --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)",
|
||||
" --header_file=\"\" - path/name of test header file to generate too"
|
||||
].join("\n")
|
||||
"\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',
|
||||
' --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',
|
||||
' --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)',
|
||||
' --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") if (!ARGV[1])
|
||||
# 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
|
||||
|
@ -1,224 +0,0 @@
|
||||
#============================================================
|
||||
# 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 GCC/Linux builds use the following:
|
||||
# make | tee Output.txt
|
||||
#
|
||||
# To use this parser use the following command
|
||||
# ruby parseOutput.rb [options] [file]
|
||||
# options: -xml : produce a JUnit compatible XML file
|
||||
# file : file to scan for results
|
||||
#============================================================
|
||||
|
||||
|
||||
class ParseOutput
|
||||
# The following flag is set to true when a test is found or false otherwise.
|
||||
@testFlag
|
||||
@xmlOut
|
||||
@arrayList
|
||||
@totalTests
|
||||
@classIndex
|
||||
|
||||
# Set the flag to indicate if there will be an XML output file or not
|
||||
def setXmlOutput()
|
||||
@xmlOut = true
|
||||
end
|
||||
|
||||
# if write our output to XML
|
||||
def writeXmlOuput()
|
||||
output = File.open("report.xml", "w")
|
||||
output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
@arrayList.each do |item|
|
||||
output << item << "\n"
|
||||
end
|
||||
output << "</testsuite>\n"
|
||||
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 testSuiteVerify(testSuiteName)
|
||||
if @testFlag == false
|
||||
@testFlag = true;
|
||||
# Split the path name
|
||||
testName = testSuiteName.split("/")
|
||||
# Remove the extension
|
||||
baseName = testName[testName.size - 1].split(".")
|
||||
@testSuite = "test." + baseName[0]
|
||||
printf "New Test: %s\n", @testSuite
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Test was flagged as having passed so format the output
|
||||
def testPassed(array)
|
||||
lastItem = array.length - 1
|
||||
testName = array[lastItem - 1]
|
||||
testSuiteVerify(array[@className])
|
||||
printf "%-40s PASS\n", testName
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
|
||||
end
|
||||
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 testPassedUnityFixture(array)
|
||||
testSuite = array[0].sub("TEST(", "")
|
||||
testSuite = testSuite.sub(",", "")
|
||||
testName = array[1].sub(")", "")
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + testSuite + "\" name=\"" + testName + "\"/>"
|
||||
end
|
||||
end
|
||||
|
||||
# Test was flagged as being ingored so format the output
|
||||
def testIgnored(array)
|
||||
lastItem = array.length - 1
|
||||
testName = array[lastItem - 2]
|
||||
reason = array[lastItem].chomp
|
||||
testSuiteVerify(array[@className])
|
||||
printf "%-40s IGNORED\n", testName
|
||||
|
||||
if testName.start_with? "TEST("
|
||||
array2 = testName.split(" ")
|
||||
@testSuite = array2[0].sub("TEST(", "")
|
||||
@testSuite = @testSuite.sub(",", "")
|
||||
testName = array2[1].sub(")", "")
|
||||
end
|
||||
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
|
||||
@arrayList.push " <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
|
||||
@arrayList.push " </testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
# Test was flagged as having failed so format the line
|
||||
def testFailed(array)
|
||||
lastItem = array.length - 1
|
||||
testName = array[lastItem - 2]
|
||||
reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
|
||||
testSuiteVerify(array[@className])
|
||||
printf "%-40s FAILED\n", testName
|
||||
|
||||
if testName.start_with? "TEST("
|
||||
array2 = testName.split(" ")
|
||||
@testSuite = array2[0].sub("TEST(", "")
|
||||
@testSuite = @testSuite.sub(",", "")
|
||||
testName = array2[1].sub(")", "")
|
||||
end
|
||||
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
|
||||
@arrayList.push " <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
|
||||
@arrayList.push " </testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Figure out what OS we are running on. For now we are assuming if it's not Windows it must
|
||||
# be Unix based.
|
||||
def detectOS()
|
||||
myOS = RUBY_PLATFORM.split("-")
|
||||
if myOS.size == 2
|
||||
if myOS[1] == "mingw32"
|
||||
@className = 1
|
||||
else
|
||||
@className = 0
|
||||
end
|
||||
else
|
||||
@className = 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Main function used to parse the file that was captured.
|
||||
def process(name)
|
||||
@testFlag = false
|
||||
@arrayList = Array.new
|
||||
|
||||
detectOS()
|
||||
|
||||
puts "Parsing file: " + name
|
||||
|
||||
|
||||
testPass = 0
|
||||
testFail = 0
|
||||
testIgnore = 0
|
||||
puts ""
|
||||
puts "=================== RESULTS ====================="
|
||||
puts ""
|
||||
File.open(name).each do |line|
|
||||
# Typical test lines look like this:
|
||||
# <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
|
||||
#
|
||||
# where path is different on Unix vs Windows devices (Windows leads with a drive letter)
|
||||
lineArray = line.split(":")
|
||||
lineSize = lineArray.size
|
||||
# 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.
|
||||
if ((lineSize >= 4) || (line.start_with? "TEST("))
|
||||
# Determine if this test passed
|
||||
if line.include? ":PASS"
|
||||
testPassed(lineArray)
|
||||
testPass += 1
|
||||
elsif line.include? ":FAIL:"
|
||||
testFailed(lineArray)
|
||||
testFail += 1
|
||||
elsif line.include? ":IGNORE:"
|
||||
testIgnored(lineArray)
|
||||
testIgnore += 1
|
||||
elsif line.start_with? "TEST("
|
||||
if line.include? " PASS"
|
||||
lineArray = line.split(" ")
|
||||
testPassedUnityFixture(lineArray)
|
||||
testPass += 1
|
||||
end
|
||||
# If none of the keywords are found there are no more tests for this suite so clear
|
||||
# the test flag
|
||||
else
|
||||
@testFlag = false
|
||||
end
|
||||
else
|
||||
@testFlag = false
|
||||
end
|
||||
end
|
||||
puts ""
|
||||
puts "=================== SUMMARY ====================="
|
||||
puts ""
|
||||
puts "Tests Passed : " + testPass.to_s
|
||||
puts "Tests Failed : " + testFail.to_s
|
||||
puts "Tests Ignored : " + testIgnore.to_s
|
||||
@totalTests = testPass + testFail + testIgnore
|
||||
if @xmlOut == true
|
||||
heading = "<testsuite tests=\"" + @totalTests.to_s + "\" failures=\"" + testFail.to_s + "\"" + " skips=\"" + testIgnore.to_s + "\">"
|
||||
@arrayList.insert(0, heading)
|
||||
writeXmlOuput()
|
||||
end
|
||||
|
||||
# return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# If the command line has no values in, used a default value of Output.txt
|
||||
parseMyFile = ParseOutput.new
|
||||
|
||||
if ARGV.size >= 1
|
||||
ARGV.each do |a|
|
||||
if a == "-xml"
|
||||
parseMyFile.setXmlOutput();
|
||||
else
|
||||
parseMyFile.process(a)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
220
auto/parse_output.rb
Normal file
220
auto/parse_output.rb
Normal file
@ -0,0 +1,220 @@
|
||||
#============================================================
|
||||
# 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 GCC/Linux builds use the following:
|
||||
# make | tee Output.txt
|
||||
#
|
||||
# To use this parser use the following command
|
||||
# ruby parseOutput.rb [options] [file]
|
||||
# options: -xml : produce a JUnit compatible XML file
|
||||
# file : file to scan for results
|
||||
#============================================================
|
||||
|
||||
class ParseOutput
|
||||
def initialize
|
||||
@test_flag = false
|
||||
@xml_out = false
|
||||
@array_list = false
|
||||
@total_tests = false
|
||||
@class_index = false
|
||||
end
|
||||
|
||||
# Set the flag to indicate if there will be an XML output file or not
|
||||
def set_xml_output
|
||||
@xml_out = true
|
||||
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
|
||||
output << "</testsuite>\n"
|
||||
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)
|
||||
return if @test_flag
|
||||
|
||||
@test_flag = true
|
||||
# Split the path name
|
||||
test_name = test_suite_name.split('/')
|
||||
# Remove the extension
|
||||
base_name = test_name[test_name.size - 1].split('.')
|
||||
@test_suite = 'test.' + base_name[0]
|
||||
printf "New Test: %s\n", @test_suite
|
||||
end
|
||||
|
||||
# Test was flagged as having passed so format the output
|
||||
def test_passed(array)
|
||||
last_item = array.length - 1
|
||||
test_name = array[last_item - 1]
|
||||
test_suite_verify(array[@class_name])
|
||||
printf "%-40s PASS\n", test_name
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '"/>'
|
||||
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)
|
||||
test_suite = array[0].sub('TEST(', '')
|
||||
test_suite = test_suite.sub(',', '')
|
||||
test_name = array[1].sub(')', '')
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + test_suite + '" name="' + test_name + '"/>'
|
||||
end
|
||||
|
||||
# Test was flagged as being ingored so format the output
|
||||
def test_ignored(array)
|
||||
last_item = array.length - 1
|
||||
test_name = array[last_item - 2]
|
||||
reason = array[last_item].chomp
|
||||
test_suite_verify(array[@class_name])
|
||||
printf "%-40s IGNORED\n", test_name
|
||||
|
||||
if test_name.start_with? 'TEST('
|
||||
array2 = test_name.split(' ')
|
||||
@test_suite = array2[0].sub('TEST(', '')
|
||||
@test_suite = @test_suite.sub(',', '')
|
||||
test_name = array2[1].sub(')', '')
|
||||
end
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">'
|
||||
@array_list.push ' <skipped type="TEST IGNORED"> ' + reason + ' </skipped>'
|
||||
@array_list.push ' </testcase>'
|
||||
end
|
||||
|
||||
# Test was flagged as having failed so format the line
|
||||
def test_failed(array)
|
||||
last_item = array.length - 1
|
||||
test_name = array[last_item - 2]
|
||||
reason = array[last_item].chomp + ' at line: ' + array[last_item - 3]
|
||||
test_suite_verify(array[@class_name])
|
||||
printf "%-40s FAILED\n", test_name
|
||||
|
||||
if test_name.start_with? 'TEST('
|
||||
array2 = test_name.split(' ')
|
||||
@test_suite = array2[0].sub('TEST(', '')
|
||||
@test_suite = @test_suite.sub(',', '')
|
||||
test_name = array2[1].sub(')', '')
|
||||
end
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">'
|
||||
@array_list.push ' <failure type="ASSERT FAILED"> ' + reason + ' </failure>'
|
||||
@array_list.push ' </testcase>'
|
||||
end
|
||||
|
||||
# Figure out what OS we are running on. For now we are assuming if it's not Windows it must
|
||||
# be Unix based.
|
||||
def detect_os
|
||||
os = RUBY_PLATFORM.split('-')
|
||||
@class_name = if os.size == 2
|
||||
if os[1] == 'mingw32'
|
||||
1
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
# Main function used to parse the file that was captured.
|
||||
def process(name)
|
||||
@test_flag = false
|
||||
@array_list = []
|
||||
|
||||
detect_os
|
||||
|
||||
puts 'Parsing file: ' + name
|
||||
|
||||
test_pass = 0
|
||||
test_fail = 0
|
||||
test_ignore = 0
|
||||
puts ''
|
||||
puts '=================== RESULTS ====================='
|
||||
puts ''
|
||||
File.open(name).each do |line|
|
||||
# Typical test lines look like this:
|
||||
# <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
|
||||
#
|
||||
# where path is different on Unix vs Windows devices (Windows leads with a drive letter)
|
||||
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.
|
||||
if (line_array.size >= 4) || (line.start_with? 'TEST(')
|
||||
# Determine if this test passed
|
||||
if line.include? ':PASS'
|
||||
test_passed(line_array)
|
||||
test_pass += 1
|
||||
elsif line.include? ':FAIL:'
|
||||
test_failed(line_array)
|
||||
test_fail += 1
|
||||
elsif line.include? ':IGNORE:'
|
||||
test_ignored(line_array)
|
||||
test_ignore += 1
|
||||
elsif line.start_with? 'TEST('
|
||||
if line.include? ' PASS'
|
||||
line_array = line.split(' ')
|
||||
test_passed_unity_fixture(line_array)
|
||||
test_pass += 1
|
||||
end
|
||||
# If none of the keywords are found there are no more tests for this suite so clear
|
||||
# the test flag
|
||||
else
|
||||
@test_flag = false
|
||||
end
|
||||
else
|
||||
@test_flag = false
|
||||
end
|
||||
end
|
||||
puts ''
|
||||
puts '=================== SUMMARY ====================='
|
||||
puts ''
|
||||
puts 'Tests Passed : ' + test_pass.to_s
|
||||
puts 'Tests Failed : ' + test_fail.to_s
|
||||
puts 'Tests Ignored : ' + test_ignore.to_s
|
||||
@total_tests = test_pass + test_fail + test_ignore
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
heading = '<testsuite tests="' + @total_tests.to_s + '" failures="' + test_fail.to_s + '"' + ' skips="' + test_ignore.to_s + '">'
|
||||
@array_list.insert(0, heading)
|
||||
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 |a|
|
||||
if a == '-xml'
|
||||
parse_my_file.set_xml_output
|
||||
else
|
||||
parse_my_file.process(a)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
@ -12,7 +12,6 @@ require 'pp'
|
||||
VERSION = 1.0
|
||||
|
||||
class ArgvParser
|
||||
|
||||
#
|
||||
# Return a structure describing the options.
|
||||
#
|
||||
@ -20,41 +19,41 @@ class ArgvParser
|
||||
# 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"
|
||||
options.results_dir = '.'
|
||||
options.root_path = '.'
|
||||
options.out_file = 'results.xml'
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: unity_to_junit.rb [options]"
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = 'Usage: unity_to_junit.rb [options]'
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Specific options:"
|
||||
o.separator ''
|
||||
o.separator 'Specific options:'
|
||||
|
||||
opts.on("-r", "--results <dir>", "Look for Unity Results files here.") do |results|
|
||||
#puts "results #{results}"
|
||||
o.on('-r', '--results <dir>', 'Look for Unity Results files here.') do |results|
|
||||
# puts "results #{results}"
|
||||
options.results_dir = results
|
||||
end
|
||||
|
||||
opts.on("-p", "--root_path <path>", "Prepend this path to files in results.") do |root_path|
|
||||
o.on('-p', '--root_path <path>', 'Prepend this path to files in results.') do |root_path|
|
||||
options.root_path = root_path
|
||||
end
|
||||
|
||||
opts.on("-o", "--output <filename>", "XML file to generate.") do |out_file|
|
||||
#puts "out_file: #{out_file}"
|
||||
o.on('-o', '--output <filename>', 'XML file to generate.') do |out_file|
|
||||
# puts "out_file: #{out_file}"
|
||||
options.out_file = out_file
|
||||
end
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Common options:"
|
||||
o.separator ''
|
||||
o.separator 'Common options:'
|
||||
|
||||
# No argument, shows at tail. This will print an options summary.
|
||||
opts.on_tail("-h", "--help", "Show this message") do
|
||||
puts opts
|
||||
o.on_tail('-h', '--help', 'Show this message') do
|
||||
puts o
|
||||
exit
|
||||
end
|
||||
|
||||
# Another typical switch to print the version.
|
||||
opts.on_tail("--version", "Show version") do
|
||||
o.on_tail('--version', 'Show version') do
|
||||
puts "unity_to_junit.rb version #{VERSION}"
|
||||
exit
|
||||
end
|
||||
@ -62,13 +61,13 @@ class ArgvParser
|
||||
|
||||
opts.parse!(args)
|
||||
options
|
||||
end # parse()
|
||||
|
||||
end # class OptparseExample
|
||||
end # parse()
|
||||
end # class OptparseExample
|
||||
|
||||
class UnityToJUnit
|
||||
include FileUtils::Verbose
|
||||
attr_reader :report, :total_tests, :failures, :ignored
|
||||
attr_writer :targets, :root, :out_file
|
||||
|
||||
def initialize
|
||||
@report = ''
|
||||
@ -77,125 +76,115 @@ class UnityToJUnit
|
||||
|
||||
def run
|
||||
# Clean up result file names
|
||||
results = @targets.map {|target| target.gsub(/\\/,"/")}
|
||||
#puts "Output File: #{@out_file}"
|
||||
f = File.new(@out_file, "w")
|
||||
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 )
|
||||
write_suites_header(f)
|
||||
results.each do |result_file|
|
||||
lines = File.readlines(result_file).map { |line| line.chomp }
|
||||
if lines.length == 0
|
||||
raise "Empty test result file: #{result_file}"
|
||||
else
|
||||
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])
|
||||
end
|
||||
#use line[0] from the test output to get the test_file path and name
|
||||
test_file_str = lines[0].gsub("\\","/")
|
||||
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
|
||||
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, ".*")
|
||||
@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 )
|
||||
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 )
|
||||
write_suites_footer(f)
|
||||
f.close
|
||||
end
|
||||
|
||||
def set_targets(target_array)
|
||||
@targets = target_array
|
||||
end
|
||||
|
||||
def set_root_path(path)
|
||||
@root = path
|
||||
end
|
||||
def set_out_file(filename)
|
||||
@out_file = filename
|
||||
end
|
||||
def usage(err_msg=nil)
|
||||
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"
|
||||
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 = get_results_structure
|
||||
|
||||
def get_details(_result_file, lines)
|
||||
results = results_structure
|
||||
lines.each do |line|
|
||||
line = line.gsub("\\","/")
|
||||
src_file,src_line,test_name,status,msg = line.split(/:/)
|
||||
line_out = ((@root and (@root != 0)) ? "#{@root}#{line}" : line ).gsub(/\//, "\\")
|
||||
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}
|
||||
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
|
||||
return results
|
||||
results
|
||||
end
|
||||
|
||||
def parse_test_summary(summary)
|
||||
if summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }
|
||||
[$1.to_i,$2.to_i,$3.to_i]
|
||||
else
|
||||
raise "Couldn't parse test results: #{summary}"
|
||||
end
|
||||
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
|
||||
|
||||
def here
|
||||
File.expand_path(File.dirname(__FILE__))
|
||||
end
|
||||
def here; File.expand_path(File.dirname(__FILE__)); end
|
||||
|
||||
private
|
||||
|
||||
def get_results_structure
|
||||
return {
|
||||
:source => {:path => '', :file => ''},
|
||||
:successes => [],
|
||||
:failures => [],
|
||||
:ignores => [],
|
||||
:counts => {:total => 0, :passed => 0, :failed => 0, :ignored => 0},
|
||||
:stdout => [],
|
||||
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 )
|
||||
def write_xml_header(stream)
|
||||
stream.puts "<?xml version='1.0' encoding='utf-8' ?>"
|
||||
end
|
||||
|
||||
def write_suites_header( stream )
|
||||
stream.puts "<testsuites>"
|
||||
def write_suites_header(stream)
|
||||
stream.puts '<testsuites>'
|
||||
end
|
||||
|
||||
def write_suite_header( counts, stream )
|
||||
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 )
|
||||
def write_failures(results, stream)
|
||||
result = results[:failures]
|
||||
result.each do |item|
|
||||
filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
|
||||
@ -206,15 +195,14 @@ class UnityToJUnit
|
||||
end
|
||||
end
|
||||
|
||||
def write_tests( results, stream )
|
||||
def write_tests(results, stream)
|
||||
result = results[:successes]
|
||||
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\" />"
|
||||
end
|
||||
end
|
||||
|
||||
def write_ignored( results, stream )
|
||||
def write_ignored(results, stream)
|
||||
result = results[:ignores]
|
||||
result.each do |item|
|
||||
filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
|
||||
@ -226,39 +214,39 @@ class UnityToJUnit
|
||||
end
|
||||
end
|
||||
|
||||
def write_suite_footer( stream )
|
||||
def write_suite_footer(stream)
|
||||
stream.puts "\t</testsuite>"
|
||||
end
|
||||
|
||||
def write_suites_footer( stream )
|
||||
stream.puts "</testsuites>"
|
||||
def write_suites_footer(stream)
|
||||
stream.puts '</testsuites>'
|
||||
end
|
||||
end #UnityToJUnit
|
||||
end # UnityToJUnit
|
||||
|
||||
if __FILE__ == $0
|
||||
#parse out the command options
|
||||
# parse out the command options
|
||||
options = ArgvParser.parse(ARGV)
|
||||
|
||||
#create an instance to work with
|
||||
# create an instance to work with
|
||||
utj = UnityToJUnit.new
|
||||
begin
|
||||
#look in the specified or current directory for result files
|
||||
targets = "#{options.results_dir.gsub(/\\/, '/')}**/*.test*"
|
||||
# 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.set_targets(results)
|
||||
utj.targets = results
|
||||
|
||||
#set the root path
|
||||
utj.set_root_path(options.root_path)
|
||||
# set the root path
|
||||
utj.root = options.root_path
|
||||
|
||||
#set the output XML file name
|
||||
#puts "Output File from options: #{options.out_file}"
|
||||
utj.set_out_file(options.out_file)
|
||||
# set the output XML file name
|
||||
# puts "Output File from options: #{options.out_file}"
|
||||
utj.out_file = options.out_file
|
||||
|
||||
#run the summarizer
|
||||
# run the summarizer
|
||||
puts utj.run
|
||||
rescue Exception => e
|
||||
rescue StandardError => e
|
||||
utj.usage e.message
|
||||
end
|
||||
end
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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'
|
||||
|
||||
@ -10,14 +10,16 @@ module RakefileHelpers
|
||||
class TestFileFilter
|
||||
def initialize(all_files = false)
|
||||
@all_files = all_files
|
||||
if not @all_files == true
|
||||
if File.exist?('test_file_filter.yml')
|
||||
filters = YAML.load_file( 'test_file_filter.yml' )
|
||||
@all_files, @only_files, @exclude_files =
|
||||
filters[:all_files], filters[:only_files], filters[:exclude_files]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false unless @all_files
|
||||
return false unless File.exist?('test_file_filter.yml')
|
||||
|
||||
filters = YAML.load_file('test_file_filter.yml')
|
||||
@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
|
||||
|
@ -1,8 +1,6 @@
|
||||
module TypeSanitizer
|
||||
|
||||
def self.sanitize_c_identifier(unsanitized)
|
||||
# convert filename to valid C identifier by replacing invalid chars with '_'
|
||||
return unsanitized.gsub(/[-\/\\\.\,\s]/, "_")
|
||||
unsanitized.gsub(/[-\/\\\.\,\s]/, '_')
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -4,7 +4,7 @@
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
#!/usr/bin/ruby
|
||||
# !/usr/bin/ruby
|
||||
#
|
||||
# unity_test_summary.rb
|
||||
#
|
||||
@ -15,37 +15,35 @@ class UnityTestSummary
|
||||
include FileUtils::Verbose
|
||||
|
||||
attr_reader :report, :total_tests, :failures, :ignored
|
||||
attr_writer :targets, :root
|
||||
|
||||
def initialize(opts = {})
|
||||
def initialize(_opts = {})
|
||||
@report = ''
|
||||
@total_tests = 0
|
||||
@failures = 0
|
||||
@ignored = 0
|
||||
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
# Clean up result file names
|
||||
results = @targets.map {|target| target.gsub(/\\/,'/')}
|
||||
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 { |line| line.chomp }
|
||||
if lines.length == 0
|
||||
raise "Empty test result file: #{result_file}"
|
||||
else
|
||||
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
|
||||
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
|
||||
@ -72,77 +70,67 @@ class UnityTestSummary
|
||||
@report += "\n"
|
||||
end
|
||||
|
||||
def set_targets(target_array)
|
||||
@targets = target_array
|
||||
end
|
||||
|
||||
def set_root_path(path)
|
||||
@root = path
|
||||
end
|
||||
|
||||
def usage(err_msg=nil)
|
||||
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."
|
||||
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 => [] }
|
||||
def get_details(_result_file, lines)
|
||||
results = { failures: [], ignores: [], successes: [] }
|
||||
lines.each do |line|
|
||||
src_file,src_line,test_name,status,msg = line.split(/:/)
|
||||
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
|
||||
_src_file, _src_line, _test_name, status, _msg = line.split(/:/)
|
||||
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
|
||||
return results
|
||||
results
|
||||
end
|
||||
|
||||
def parse_test_summary(summary)
|
||||
if summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }
|
||||
[$1.to_i,$2.to_i,$3.to_i]
|
||||
else
|
||||
raise "Couldn't parse test results: #{summary}"
|
||||
end
|
||||
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
|
||||
|
||||
def here; File.expand_path(File.dirname(__FILE__)); end
|
||||
|
||||
def here
|
||||
File.expand_path(File.dirname(__FILE__))
|
||||
end
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
|
||||
#parse out the command options
|
||||
opts, args = ARGV.partition {|v| v =~ /^--\w+/}
|
||||
opts.map! {|v| v[2..-1].to_sym }
|
||||
# parse out the command options
|
||||
opts, args = ARGV.partition { |v| v =~ /^--\w+/ }
|
||||
opts.map! { |v| v[2..-1].to_sym }
|
||||
|
||||
#create an instance to work with
|
||||
# create an instance to work with
|
||||
uts = UnityTestSummary.new(opts)
|
||||
|
||||
begin
|
||||
#look in the specified or current directory for result files
|
||||
# look in the specified or current directory for result files
|
||||
args[0] ||= './'
|
||||
targets = "#{ARGV[0].gsub(/\\/, '/')}**/*.test*"
|
||||
targets = "#{ARGV[0].tr('\\', '/')}**/*.test*"
|
||||
results = Dir[targets]
|
||||
raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?
|
||||
uts.set_targets(results)
|
||||
uts.targets = results
|
||||
|
||||
#set the root path
|
||||
# set the root path
|
||||
args[1] ||= Dir.pwd + '/'
|
||||
uts.set_root_path(ARGV[1])
|
||||
uts.root = ARGV[1]
|
||||
|
||||
#run the summarizer
|
||||
# run the summarizer
|
||||
puts uts.run
|
||||
rescue Exception => e
|
||||
rescue StandardError => e
|
||||
uts.usage e.message
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user