GICodeWarrior-ci_reporter 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ # (c) Copyright 2006-2007 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ namespace :ci do
6
+ namespace :setup do
7
+ task :testunit do
8
+ rm_rf ENV["CI_REPORTS"] || "test/reports"
9
+ ENV["TESTOPTS"] = "#{ENV["TESTOPTS"]} #{File.dirname(__FILE__)}/test_unit_loader.rb"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ # (c) Copyright 2006-2007 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ $: << File.dirname(__FILE__) + "/../../.."
6
+ require 'ci/reporter/test_unit'
7
+
8
+ module Test #:nodoc:all
9
+ module Unit
10
+ module UI
11
+ module Console
12
+ class TestRunner
13
+ def create_mediator(suite)
14
+ # swap in our custom mediator
15
+ return CI::Reporter::TestUnit.new(suite)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # (c) Copyright 2006-2007 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require 'fileutils'
6
+
7
+ module CI #:nodoc:
8
+ module Reporter #:nodoc:
9
+ class ReportManager
10
+ def initialize(prefix)
11
+ @basedir = ENV['CI_REPORTS'] || File.expand_path("#{Dir.getwd}/#{prefix.downcase}/reports")
12
+ @basename = "#{@basedir}/#{prefix.upcase}"
13
+ FileUtils.mkdir_p(@basedir)
14
+ end
15
+
16
+ def write_report(suite)
17
+ File.open("#{@basename}-#{suite.name.gsub(/[^a-zA-Z0-9]+/, '-')}.xml", "w") do |f|
18
+ f << suite.to_xml
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,127 @@
1
+ # (c) Copyright 2006-2009 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require 'ci/reporter/core'
6
+ tried_gem = false
7
+ begin
8
+ require 'spec'
9
+ require 'spec/runner/formatter/specdoc_formatter'
10
+ rescue LoadError
11
+ unless tried_gem
12
+ tried_gem = true
13
+ require 'rubygems'
14
+ gem 'rspec'
15
+ retry
16
+ end
17
+ end
18
+
19
+ module CI
20
+ module Reporter
21
+ # Wrapper around a <code>RSpec</code> error or failure to be used by the test suite to interpret results.
22
+ class RSpecFailure
23
+ def initialize(failure)
24
+ @failure = failure
25
+ end
26
+
27
+ def failure?
28
+ @failure.expectation_not_met?
29
+ end
30
+
31
+ def error?
32
+ !@failure.expectation_not_met?
33
+ end
34
+
35
+ def name() @failure.exception.class.name end
36
+ def message() @failure.exception.message end
37
+ def location() @failure.exception.backtrace.join("\n") end
38
+ end
39
+
40
+ # Custom +RSpec+ formatter used to hook into the spec runs and capture results.
41
+ class RSpec < Spec::Runner::Formatter::BaseFormatter
42
+ attr_accessor :report_manager
43
+ def initialize(*args)
44
+ super
45
+ @report_manager = ReportManager.new("spec")
46
+ @suite = nil
47
+ end
48
+
49
+ def start(spec_count)
50
+ end
51
+
52
+ # rspec 0.9
53
+ def add_behaviour(name)
54
+ new_suite(name)
55
+ end
56
+
57
+ # Compatibility with rspec < 1.2.4
58
+ def add_example_group(example_group)
59
+ new_suite(example_group.description)
60
+ end
61
+
62
+ # rspec >= 1.2.4
63
+ def example_group_started(example_group)
64
+ new_suite(example_group.description)
65
+ end
66
+
67
+ def example_started(name)
68
+ name = name.description if name.respond_to?(:description)
69
+ spec = TestCase.new name
70
+ @suite.testcases << spec
71
+ spec.start
72
+ end
73
+
74
+ def example_failed(name, counter, failure)
75
+ spec = @suite.testcases.last
76
+ spec.finish
77
+ spec.failures << RSpecFailure.new(failure)
78
+ end
79
+
80
+ def example_passed(name)
81
+ spec = @suite.testcases.last
82
+ spec.finish
83
+ end
84
+
85
+ def example_pending(*args)
86
+ spec = @suite.testcases.last
87
+ spec.finish
88
+ spec.name = "#{spec.name} (PENDING)"
89
+ end
90
+
91
+ def start_dump
92
+ end
93
+
94
+ def dump_failure(*args)
95
+ end
96
+
97
+ def dump_summary(*args)
98
+ write_report
99
+ end
100
+
101
+ def dump_pending
102
+ end
103
+
104
+ def close
105
+ end
106
+
107
+ private
108
+ def write_report
109
+ @suite.finish
110
+ @report_manager.write_report(@suite)
111
+ end
112
+
113
+ def new_suite(name)
114
+ write_report if @suite
115
+ @suite = TestSuite.new name
116
+ @suite.start
117
+ end
118
+ end
119
+
120
+ class RSpecDoc < RSpec
121
+ def initialize(*args)
122
+ @formatter = Spec::Runner::Formatter::SpecdocFormatter.new(*args)
123
+ super
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,147 @@
1
+ # (c) Copyright 2006-2007 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require 'delegate'
6
+ require 'stringio'
7
+
8
+ module CI
9
+ module Reporter
10
+ # Emulates/delegates IO to $stdout or $stderr in order to capture output to report in the XML file.
11
+ class OutputCapture < DelegateClass(IO)
12
+ # Start capturing IO, using the given block to assign self to the proper IO global.
13
+ def initialize(io, &assign)
14
+ super
15
+ @delegate_io = io
16
+ @captured_io = StringIO.new
17
+ @assign_block = assign
18
+ @assign_block.call self
19
+ end
20
+
21
+ # Finalize the capture and reset to the original IO object.
22
+ def finish
23
+ @assign_block.call @delegate_io
24
+ @captured_io.string
25
+ end
26
+
27
+ # setup tee methods
28
+ %w(<< print printf putc puts write).each do |m|
29
+ module_eval(<<-EOS, __FILE__, __LINE__)
30
+ def #{m}(*args, &block)
31
+ @delegate_io.send(:#{m}, *args, &block)
32
+ @captured_io.send(:#{m}, *args, &block)
33
+ end
34
+ EOS
35
+ end
36
+ end
37
+
38
+ # Basic structure representing the running of a test suite. Used to time tests and store results.
39
+ class TestSuite < Struct.new(:name, :tests, :time, :failures, :errors, :assertions)
40
+ attr_accessor :testcases
41
+ attr_accessor :stdout, :stderr
42
+ def initialize(name)
43
+ super(name.to_s) # RSpec passes a "description" object instead of a string
44
+ @testcases = []
45
+ end
46
+
47
+ # Starts timing the test suite.
48
+ def start
49
+ @start = Time.now
50
+ unless ENV['CI_CAPTURE'] == "off"
51
+ @capture_out = OutputCapture.new($stdout) {|io| $stdout = io }
52
+ @capture_err = OutputCapture.new($stderr) {|io| $stderr = io }
53
+ end
54
+ end
55
+
56
+ # Finishes timing the test suite.
57
+ def finish
58
+ self.tests = testcases.size
59
+ self.time = Time.now - @start
60
+ self.failures = testcases.inject(0) {|sum,tc| sum += tc.failures.select{|f| f.failure? }.size }
61
+ self.errors = testcases.inject(0) {|sum,tc| sum += tc.failures.select{|f| f.error? }.size }
62
+ self.stdout = @capture_out.finish if @capture_out
63
+ self.stderr = @capture_err.finish if @capture_err
64
+ end
65
+
66
+ # Creates the xml builder instance used to create the report xml document.
67
+ def create_builder
68
+ begin
69
+ require 'rubygems'
70
+ gem 'builder'
71
+ rescue LoadError
72
+ end
73
+
74
+ require 'builder'
75
+ # :escape_attrs is obsolete in a newer version, but should do no harm
76
+ Builder::XmlMarkup.new(:indent => 2, :escape_attrs => true)
77
+ end
78
+
79
+ # Creates an xml string containing the test suite results.
80
+ def to_xml
81
+ builder = create_builder
82
+ # more recent version of Builder doesn't need the escaping
83
+ def builder.trunc!(txt)
84
+ txt.sub(/\n.*/m, '...')
85
+ end
86
+ builder.instruct!
87
+ attrs = {}
88
+ each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) unless v.nil? || v.to_s.empty? }
89
+ builder.testsuite(attrs) do
90
+ @testcases.each do |tc|
91
+ tc.to_xml(builder)
92
+ end
93
+ builder.tag! "system-out" do
94
+ builder.cdata! self.stdout
95
+ end
96
+ builder.tag! "system-err" do
97
+ builder.cdata! self.stderr
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ # Structure used to represent an individual test case. Used to time the test and store the result.
104
+ class TestCase < Struct.new(:name, :time, :assertions)
105
+ attr_accessor :failures
106
+
107
+ def initialize(*args)
108
+ super
109
+ @failures = []
110
+ end
111
+
112
+ # Starts timing the test.
113
+ def start
114
+ @start = Time.now
115
+ end
116
+
117
+ # Finishes timing the test.
118
+ def finish
119
+ self.time = Time.now - @start
120
+ end
121
+
122
+ # Returns non-nil if the test failed.
123
+ def failure?
124
+ !failures.empty? && failures.detect {|f| f.failure? }
125
+ end
126
+
127
+ # Returns non-nil if the test had an error.
128
+ def error?
129
+ !failures.empty? && failures.detect {|f| f.error? }
130
+ end
131
+
132
+ # Writes xml representing the test result to the provided builder.
133
+ def to_xml(builder)
134
+ attrs = {}
135
+ each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) unless v.nil? || v.to_s.empty?}
136
+ builder.testcase(attrs) do
137
+ failures.each do |failure|
138
+ builder.failure(:type => builder.trunc!(failure.name), :message => builder.trunc!(failure.message)) do
139
+ builder.text!(failure.message + " (#{failure.name})\n")
140
+ builder.text!(failure.location)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,124 @@
1
+ # (c) Copyright 2006-2007 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require 'ci/reporter/core'
6
+ require 'test/unit'
7
+ require 'test/unit/ui/console/testrunner'
8
+
9
+ module CI
10
+ module Reporter
11
+ # Factory for constructing either a CI::Reporter::TestUnitFailure or CI::Reporter::TestUnitError depending on the result
12
+ # of the test.
13
+ class Failure
14
+ def self.new(fault)
15
+ fault.kind_of?(Test::Unit::Failure) ? TestUnitFailure.new(fault) : TestUnitError.new(fault)
16
+ end
17
+ end
18
+
19
+ # Wrapper around a <code>Test::Unit</code> error to be used by the test suite to interpret results.
20
+ class TestUnitError
21
+ def initialize(fault)
22
+ @fault = fault
23
+ end
24
+ def failure?() false end
25
+ def error?() true end
26
+ def name() @fault.exception.class.name end
27
+ def message() @fault.exception.message end
28
+ def location() @fault.exception.backtrace.join("\n") end
29
+ end
30
+
31
+ # Wrapper around a <code>Test::Unit</code> failure to be used by the test suite to interpret results.
32
+ class TestUnitFailure
33
+ def initialize(fault)
34
+ @fault = fault
35
+ end
36
+ def failure?() true end
37
+ def error?() false end
38
+ def name() Test::Unit::AssertionFailedError.name end
39
+ def message() @fault.message end
40
+ def location() @fault.location.join("\n") end
41
+ end
42
+
43
+ # Replacement Mediator that adds listeners to capture the results of the <code>Test::Unit</code> runs.
44
+ class TestUnit < Test::Unit::UI::TestRunnerMediator
45
+ def initialize(suite, report_mgr = nil)
46
+ super(suite)
47
+ @report_manager = report_mgr || ReportManager.new("test")
48
+ add_listener(Test::Unit::UI::TestRunnerMediator::STARTED, &method(:started))
49
+ add_listener(Test::Unit::TestCase::STARTED, &method(:test_started))
50
+ add_listener(Test::Unit::TestCase::FINISHED, &method(:test_finished))
51
+ add_listener(Test::Unit::TestResult::FAULT, &method(:fault))
52
+ add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED, &method(:finished))
53
+ end
54
+
55
+ def started(result)
56
+ @suite_result = result
57
+ @last_assertion_count = 0
58
+ @current_suite = nil
59
+ @unknown_count = 0
60
+ @result_assertion_count = 0
61
+ end
62
+
63
+ def test_started(name)
64
+ test_name, suite_name = extract_names(name)
65
+ unless @current_suite && @current_suite.name == suite_name
66
+ finish_suite
67
+ start_suite(suite_name)
68
+ end
69
+ start_test(test_name)
70
+ end
71
+
72
+ def test_finished(name)
73
+ finish_test
74
+ end
75
+
76
+ def fault(fault)
77
+ tc = @current_suite.testcases.last
78
+ tc.failures << Failure.new(fault)
79
+ end
80
+
81
+ def finished(elapsed_time)
82
+ finish_suite
83
+ end
84
+
85
+ private
86
+ def extract_names(name)
87
+ match = name.match(/(.*)\(([^)]*)\)/)
88
+ if match
89
+ [match[1], match[2]]
90
+ else
91
+ @unknown_count += 1
92
+ [name, "unknown-#{@unknown_count}"]
93
+ end
94
+ end
95
+
96
+ def start_suite(suite_name)
97
+ @current_suite = TestSuite.new(suite_name)
98
+ @current_suite.start
99
+ end
100
+
101
+ def finish_suite
102
+ if @current_suite
103
+ @current_suite.finish
104
+ @current_suite.assertions = @suite_result.assertion_count - @last_assertion_count
105
+ @last_assertion_count = @suite_result.assertion_count
106
+ @report_manager.write_report(@current_suite)
107
+ end
108
+ end
109
+
110
+ def start_test(test_name)
111
+ tc = TestCase.new(test_name)
112
+ tc.start
113
+ @current_suite.testcases << tc
114
+ end
115
+
116
+ def finish_test
117
+ tc = @current_suite.testcases.last
118
+ tc.finish
119
+ tc.assertions = @suite_result.assertion_count - @result_assertion_count
120
+ @result_assertion_count = @suite_result.assertion_count
121
+ end
122
+ end
123
+ end
124
+ end