#!/usr/bin/env ruby # == Synopsis # # OCUnitReport: parses xcodebuild test report output into xml and # creates junit compatible files # # == Usage # # ./OCUnitReport [OPTIONS] # # --report-dir, -d:: The directory to create the reports in (defaults to .) # Copyright (c) 2007, Joris Kluivers # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY # WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. require "rubygems" # example xml http://www.photovault.org/cgi-bin/trac.cgi/browser/branches/extvols/reports/TESTS-TestSuites.xml?rev=354&format=txt # more info http://junitpdfreport.sourceforge.net/manual/junitreport_xslt_description/ # sudo /opt/local/bin/gem install extensions builder getopt # sudo port install rb-xslt require "builder" require "extensions/string" require "getoptlong" require "rdoc/usage" require "xml/xslt" dir = "." # search for command line options opts = GetoptLong.new( [ '--help', '-h', GetoptLong::NO_ARGUMENT], [ '--report-dir', '-d', GetoptLong::REQUIRED_ARGUMENT] ) begin opts.each do |opt,arg| case opt when '--report-dir' dir = arg when '--help' RDoc::usage() exit end end rescue RDoc::usage() exit end # classes for TestSuites and Test cases class TestSuite attr_accessor :name def initialize(name) @tests = [] self.name = name end def add_test(test) @tests << test end def test_count return @tests.length end def each_test return @tests.each { |test| yield test } end def failures return @tests.inject(0) do |failures, test| if test.failure? failures += 1 else failures += 0 end end end def total_time return @tests.inject(0) do |total, test| total += test.time.to_f end end def to_s return self.name end end class TestCase attr_accessor :name, :state, :time def initialize(name, state = :passed) self.name = name self.state = state self.time = nil end def self.states @states ||= [:passed, :failed] end def failure? return self.state == :failed end end lines = STDIN.readlines current_suite = nil test_suites = [] lines.each { |line| if line.starts_with?("===") end if line.starts_with?("Test Suite") # print "Test Suite\n" line.scan(/^Test Suite \'(.*)\' (started|finished) at/i) { |matches| if matches[1] == "started" current_suite = TestSuite.new(matches[0]) elsif matches[1] == "finished" and current_suite != nil test_suites << current_suite current_suite = nil end } end if line.starts_with?("Test Case") line.scan(/^Test Case \'(.*)\' (passed|failed) \(([0-9]+\.[0-9]+) seconds\)/) { |matches| test_case = TestCase.new(matches[0], matches[1].intern) test_case.time = matches[2] current_suite.add_test test_case } end if line.starts_with?("Executed") line.scan(/^Executed ([0-9]+) tests, with ([0-9]+) failures \(([0-9]+) unexpected\)/) { |matches| } end } # generate TEST-*.xml file for each TestSuite test_suites.each { |suite| output_f = File.new(dir + "/TEST-" + suite.name + ".xml", "w") xml = Builder::XmlMarkup.new(:target => output_f, :indent => 4) xml.instruct! xml.testsuite(:errors => 0, :id => 1, :skipped => 0, :failures => suite.failures, :name => suite.name, :tests => suite.test_count, :time => suite.total_time) { suite.each_test { |test| if test.failure? xml.testcase(:name => test.name, :time => test.time) { xml.failure("some failure text", :type => "unknown") } else xml.testcase(:name => test.name, :time => test.time) end } xml.tag!("system-out") xml.tag!("system-err") } }