Created
September 30, 2009 10:14
-
-
Save faultier/197966 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# vim: encoding=utf-8 filetype=ruby | |
require 'rubygems' | |
require 'optparse' | |
require 'yaml' | |
options = YAML.load(<<CONF) | |
color: true | |
verbose: false | |
target: 'UnitTest' | |
config: 'Release' | |
sdkversion: '3.0' | |
format: 'simple' | |
CONF | |
if FileTest.exist?('.rocurc') | |
options.merge!(YAML.load_file('.rocurc')) | |
end | |
OptionParser.new { |opt| | |
opt.on('-o','--options=FILE') { |f| options.merge!(YAML.load_file(f)) } | |
opt.on('--[no-]color') { |c| options['color'] = c } | |
opt.on('--[no-]verbose') { |v| options['verbose'] = v } | |
opt.on('-t','--target=TARGET') { |t| options['target'] = t } | |
opt.on('--sdk-version=VERSION') { |v| options['sdkversion'] = v } | |
opt.on('-f','--format=FORMAT') { |f| options['format'] = v } | |
opt.parse!(ARGV) | |
} | |
TESTCMD = "xcodebuild -target #{options['target']} -configuration #{options['config']} -sdk iphonesimulator#{options['sdkversion']} 2>&1" | |
module DummyANSIColor | |
def bold; self; end | |
def negative; self; end | |
def red; self; end | |
def green; self; end | |
def yellow; self; end | |
def blue; self; end | |
end | |
if options['color'] | |
begin | |
require 'term/ansicolor' | |
class String | |
include Term::ANSIColor | |
end | |
rescue LoadError | |
warn "#{$0} depends on Term::ANSIColor!" | |
end | |
end | |
unless String.instance_methods.include?(:bold) | |
class String | |
include DummyANSIColor | |
end | |
end | |
module XBTools | |
class Filter | |
Terminator = /\A\*\* BUILD/ | |
def self.process(io, opts={}) | |
new(io, opts).process | |
end | |
def initialize(io,opts={}) | |
@input = io | |
@log = [] | |
@opts = opts | |
end | |
def parse | |
line = @input.gets | |
case line | |
when Terminator | |
false | |
else | |
@log.push({ :message => line, :type => :unknown }) | |
end | |
end | |
def fold | |
@log.map{ |l| | |
line = l[:position] ? "#{l[:message]} (#{l[:position]})" : l[:message] | |
line = case l[:type] | |
when :header then line.bold | |
when :passed then line.green | |
when :failed then line.red | |
else line end | |
line = case l[:level] | |
when :error then line.red.bold | |
when :warning then line.yellow | |
else line end | |
l[:negative] ? line.negative : line | |
}.join("\n") | |
end | |
def process | |
while parse; end | |
fold | |
end | |
end | |
class BuildFilter < XBTools::Filter | |
Terminator = /\APhaseScriptExecution/ | |
def initialize(io,opts={}) | |
super | |
@in_submsg = false | |
end | |
def parse | |
return false unless super | |
line = @log.pop | |
return false unless line | |
case line[:message] | |
when /\A(?:#{Dir.pwd}\/)?(.+); In function (.+)/ | |
@log.push( :message => $2, :position => $1, :type => :info ) | |
when /\A(?:#{Dir.pwd}\/)?(.+[0-9]+): (warning|error): (.+)/ | |
pos = $1 | |
level = $2.intern | |
msg = $3 | |
if @in_submsg | |
@log.last[:message] << msg | |
@in_submsg = false if msg =~ /\)(\n|\z)/ | |
elsif msg =~ /\A\(/ | |
@log.last[:message] << ' ' << msg | |
@in_submsg = true | |
else | |
@log.push( :message => msg, :position => pos, :level => level ) | |
end | |
when Terminator | |
return false | |
end | |
true | |
end | |
end | |
class OCUnitFilter < Filter | |
def initialize(io,opts={}) | |
super | |
@in_subtest = false | |
@failures = [] | |
@opts[:format] ||= 'simple' | |
end | |
def fold | |
if @opts[:format] == 'specdoc' | |
super | |
else | |
log = {} | |
current = '' | |
result = nil | |
if (@log.last)[:message] =~ /Executed/ | |
result = @log.pop | |
end | |
@log.each do |l| | |
if l[:type] == :header | |
current = l[:message] | |
log[current] = { | |
:results => [], | |
:errors => [] | |
} | |
else | |
section = log[current] | |
if !!section | |
msg = l[:position] ? "#{l[:message]} (#{l[:position]})" : l[:message] | |
msg.gsub!(/\t/,'') unless msg.nil? | |
case l[:type] | |
when :passed | |
section[:results] << '.'.green | |
when :failed | |
section[:results] << 'E'.red | |
section[:errors] << msg.red.bold | |
else | |
case l[:level] | |
when :error | |
section[:errors] << msg.red.bold | |
when :warning | |
section[:errors] << msg.yellow | |
end | |
end | |
end | |
end | |
end | |
output = "" | |
errors = [] | |
log.each_pair do |key,val| | |
output << val[:results].join('') | |
unless val[:errors].empty? | |
errors << [key.bold, *val[:errors]].join("\n") | |
end | |
end | |
output = "#{output}\n\n#{errors.join("\n\n")}" | |
output << "\n\n#{(result[:type] == :passed ? result[:message].green : result[:message].red).negative}" if !!result | |
output | |
end | |
end | |
def parse | |
return false unless super | |
line = @log.pop | |
return false unless line | |
case line[:message] | |
when /\A(?:#{Dir.pwd}\/)?(.+[0-9]+): error: -\[.+\] : (.+)/ | |
pos = $1 | |
msg = $2 | |
if @failures.last && @failures.last[:type] == 'assert' && [email protected][:message] | |
@failures.last[:message] = "Assertion failure. #{msg} (#{@failures.last[:position]})" | |
elsif pos =~ /Unknown.m:0/ | |
@failures << { :type => 'runtime', :message => msg } | |
else | |
@failures << { :type => 'test', :message => "#{msg} (#{pos})" } | |
end | |
when /\*\*\* Assertion failure in (?:-|\+)\[.+\], (?:#{Dir.pwd}\/)?(.+)/ | |
@failures.push( :type => 'assert', :position => $1 ) | |
when /\AExecuted/ | |
if @in_subtest | |
@in_subtest = false | |
else | |
msg = line[:message] | |
@log.push( | |
:message => msg, | |
:type => msg =~ /with 0 failure/ ? :passed : :failed, | |
:negative => true | |
) | |
end | |
when /\ATest Suite '([a-zA-Z0-9]+)' started/ | |
@log.push( :message => "#{$1}:", :type => :header ) | |
@in_subtest = true | |
when /\ATest Suite '(.+)' started/ | |
@log.push( :message => "#{$1}:", :type => :header ) | |
when /\ATest Case '(-\[.+\])' passed/ | |
@log.push( :message => "\t#{$1}", :type => :passed ) | |
when /\ATest Case '(-\[.+\])' failed/ | |
@log.push( :message => "\t#{$1}", :type => :failed ) | |
@failures.each do |assert| | |
case assert[:type] | |
when 'test' | |
@log.push( :message => "\t\t#{assert[:message]}", :type => :failed ) | |
when 'assert' | |
@log.push( :message => "\t\t#{assert[:message]}", :level => :warning ) | |
when 'runtime' | |
@log.push( :message => "\t\t#{assert[:message]}", :level => :error ) | |
end | |
end | |
@failures = [] | |
when /\ATest Suite '.+' finished/ | |
@log.push( :message => 'All test finished.' ) unless @in_subtest | |
end | |
true | |
end | |
def message(m) | |
end | |
private :message | |
end | |
end | |
if $0 == __FILE__ | |
IO.popen(TESTCMD) { |io| | |
puts '=== BUILD PHASE ===' | |
puts XBTools::BuildFilter.process(io) | |
puts '=== TEST PHASE ===' | |
puts XBTools::OCUnitFilter.process(io, :format => options['format']) | |
} | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment