ci_reporter 1.0 → 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +141 -1
- data/LICENSE.txt +21 -0
- data/Manifest.txt +10 -0
- data/README.txt +40 -20
- data/Rakefile +93 -25
- data/lib/ci/reporter/core.rb +5 -1
- data/lib/ci/reporter/cucumber.rb +125 -0
- data/lib/ci/reporter/rake/cucumber.rb +19 -0
- data/lib/ci/reporter/rake/cucumber_loader.rb +6 -0
- data/lib/ci/reporter/rake/rspec.rb +18 -2
- data/lib/ci/reporter/rake/rspec_loader.rb +6 -7
- data/lib/ci/reporter/rake/test_unit.rb +8 -1
- data/lib/ci/reporter/rake/test_unit_loader.rb +21 -7
- data/lib/ci/reporter/rake/utils.rb +14 -0
- data/lib/ci/reporter/report_manager.rb +40 -2
- data/lib/ci/reporter/rspec.rb +154 -30
- data/lib/ci/reporter/test_suite.rb +81 -33
- data/lib/ci/reporter/test_unit.rb +42 -12
- data/lib/ci/reporter/version.rb +5 -0
- data/spec/ci/reporter/cucumber_spec.rb +230 -0
- data/spec/ci/reporter/output_capture_spec.rb +57 -0
- data/spec/ci/reporter/rake/rake_tasks_spec.rb +100 -0
- data/spec/ci/reporter/report_manager_spec.rb +14 -10
- data/spec/ci/reporter/rspec_spec.rb +136 -23
- data/spec/ci/reporter/test_suite_spec.rb +61 -26
- data/spec/ci/reporter/test_unit_spec.rb +94 -17
- data/spec/spec_helper.rb +16 -4
- data/stub.rake +14 -0
- data/tasks/ci_reporter.rake +7 -1
- metadata +117 -55
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
|
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
|
3
|
+
# software license details.
|
|
4
|
+
|
|
5
|
+
module CI
|
|
6
|
+
module Reporter
|
|
7
|
+
def self.maybe_quote_filename(fn)
|
|
8
|
+
if fn =~ /\s/
|
|
9
|
+
fn = %{"#{fn}"}
|
|
10
|
+
end
|
|
11
|
+
fn
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
|
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
|
3
|
+
# software license details.
|
|
4
|
+
|
|
1
5
|
require 'fileutils'
|
|
2
6
|
|
|
3
7
|
module CI #:nodoc:
|
|
@@ -10,10 +14,44 @@ module CI #:nodoc:
|
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
def write_report(suite)
|
|
13
|
-
File.open(
|
|
17
|
+
File.open(filename_for(suite), "w") do |f|
|
|
14
18
|
f << suite.to_xml
|
|
15
19
|
end
|
|
16
20
|
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# creates a uniqe filename per suite
|
|
26
|
+
# to prevent results from being overwritten
|
|
27
|
+
# if a result file is already written, it appends an index
|
|
28
|
+
# e.g.
|
|
29
|
+
# SPEC-MailsController.xml
|
|
30
|
+
# SPEC-MailsController.0.xml
|
|
31
|
+
# SPEC-MailsController.1.xml
|
|
32
|
+
# SPEC-MailsController...xml
|
|
33
|
+
# SPEC-MailsController.N.xml
|
|
34
|
+
#
|
|
35
|
+
# with N < 100000, to prevent endless sidestep loops
|
|
36
|
+
MAX_SIDESTEPS = 100000
|
|
37
|
+
#
|
|
38
|
+
def filename_for(suite)
|
|
39
|
+
basename = "#{@basename}-#{suite.name.gsub(/[^a-zA-Z0-9]+/, '-')}"
|
|
40
|
+
suffix = "xml"
|
|
41
|
+
|
|
42
|
+
# the initial filename, e.g. SPEC-MailsController.xml
|
|
43
|
+
filename = [basename, suffix].join(".")
|
|
44
|
+
|
|
45
|
+
# if the initial filename is already in use
|
|
46
|
+
# do sidesteps, beginning with SPEC-MailsController.0.xml
|
|
47
|
+
i = 0
|
|
48
|
+
while File.exists?(filename) && i < MAX_SIDESTEPS
|
|
49
|
+
filename = [basename, i, suffix].join(".")
|
|
50
|
+
i += 1
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
filename
|
|
54
|
+
end
|
|
17
55
|
end
|
|
18
56
|
end
|
|
19
|
-
end
|
|
57
|
+
end
|
data/lib/ci/reporter/rspec.rb
CHANGED
|
@@ -1,13 +1,38 @@
|
|
|
1
|
+
# Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
|
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
|
3
|
+
# software license details.
|
|
4
|
+
|
|
1
5
|
require 'ci/reporter/core'
|
|
2
|
-
gem 'rspec'
|
|
3
|
-
require 'spec'
|
|
4
6
|
|
|
5
7
|
module CI
|
|
6
8
|
module Reporter
|
|
9
|
+
module RSpecFormatters
|
|
10
|
+
begin
|
|
11
|
+
require 'rspec/core/formatters/base_formatter'
|
|
12
|
+
require 'rspec/core/formatters/progress_formatter'
|
|
13
|
+
require 'rspec/core/formatters/documentation_formatter'
|
|
14
|
+
BaseFormatter = ::RSpec::Core::Formatters::BaseFormatter
|
|
15
|
+
ProgressFormatter = ::RSpec::Core::Formatters::ProgressFormatter
|
|
16
|
+
DocFormatter = ::RSpec::Core::Formatters::DocumentationFormatter
|
|
17
|
+
rescue LoadError => first_error
|
|
18
|
+
begin
|
|
19
|
+
require 'spec/runner/formatter/progress_bar_formatter'
|
|
20
|
+
require 'spec/runner/formatter/specdoc_formatter'
|
|
21
|
+
BaseFormatter = ::Spec::Runner::Formatter::BaseFormatter
|
|
22
|
+
ProgressFormatter = ::Spec::Runner::Formatter::ProgressBarFormatter
|
|
23
|
+
DocFormatter = ::Spec::Runner::Formatter::SpecdocFormatter
|
|
24
|
+
rescue LoadError
|
|
25
|
+
raise first_error
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
7
30
|
# Wrapper around a <code>RSpec</code> error or failure to be used by the test suite to interpret results.
|
|
8
31
|
class RSpecFailure
|
|
32
|
+
attr_reader :exception
|
|
9
33
|
def initialize(failure)
|
|
10
34
|
@failure = failure
|
|
35
|
+
@exception = failure.exception
|
|
11
36
|
end
|
|
12
37
|
|
|
13
38
|
def failure?
|
|
@@ -15,71 +40,170 @@ module CI
|
|
|
15
40
|
end
|
|
16
41
|
|
|
17
42
|
def error?
|
|
18
|
-
|
|
43
|
+
!failure?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def name() exception.class.name end
|
|
47
|
+
def message() exception.message end
|
|
48
|
+
def location() (exception.backtrace || ["No backtrace available"]).join("\n") end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class RSpec2Failure < RSpecFailure
|
|
52
|
+
def initialize(example, formatter)
|
|
53
|
+
@formatter = formatter
|
|
54
|
+
@example = example
|
|
55
|
+
@exception = @example.execution_result[:exception] || @example.execution_result[:exception_encountered]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def failure?
|
|
59
|
+
exception.is_a?(::RSpec::Expectations::ExpectationNotMetError)
|
|
19
60
|
end
|
|
20
61
|
|
|
21
|
-
def
|
|
22
|
-
|
|
23
|
-
|
|
62
|
+
def location()
|
|
63
|
+
output = []
|
|
64
|
+
output.push "#{exception.class.name << ":"}" unless exception.class.name =~ /RSpec/
|
|
65
|
+
output.push @exception.message
|
|
66
|
+
|
|
67
|
+
@formatter.format_backtrace(@exception.backtrace, @example).each do |backtrace_info|
|
|
68
|
+
output.push " #{backtrace_info}"
|
|
69
|
+
end
|
|
70
|
+
output.join "\n"
|
|
71
|
+
end
|
|
24
72
|
end
|
|
25
73
|
|
|
26
74
|
# Custom +RSpec+ formatter used to hook into the spec runs and capture results.
|
|
27
|
-
class RSpec <
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
75
|
+
class RSpec < RSpecFormatters::BaseFormatter
|
|
76
|
+
attr_accessor :report_manager
|
|
77
|
+
attr_accessor :formatter
|
|
78
|
+
def initialize(*args)
|
|
79
|
+
super
|
|
80
|
+
@formatter ||= RSpecFormatters::ProgressFormatter.new(*args)
|
|
81
|
+
@report_manager = ReportManager.new("spec")
|
|
31
82
|
@suite = nil
|
|
32
83
|
end
|
|
33
84
|
|
|
34
85
|
def start(spec_count)
|
|
35
|
-
|
|
86
|
+
@formatter.start(spec_count)
|
|
36
87
|
end
|
|
37
88
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@suite.start
|
|
89
|
+
# rspec 0.9
|
|
90
|
+
def add_behaviour(name)
|
|
91
|
+
@formatter.add_behaviour(name)
|
|
92
|
+
new_suite(name)
|
|
43
93
|
end
|
|
44
94
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
95
|
+
# Compatibility with rspec < 1.2.4
|
|
96
|
+
def add_example_group(example_group)
|
|
97
|
+
@formatter.add_example_group(example_group)
|
|
98
|
+
new_suite(description_for(example_group))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# rspec >= 1.2.4
|
|
102
|
+
def example_group_started(example_group)
|
|
103
|
+
@formatter.example_group_started(example_group)
|
|
104
|
+
new_suite(description_for(example_group))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def example_group_finished(example_group)
|
|
108
|
+
@formatter.example_group_finished(example_group)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def example_started(name_or_example)
|
|
112
|
+
@formatter.example_started(name_or_example)
|
|
113
|
+
spec = TestCase.new
|
|
48
114
|
@suite.testcases << spec
|
|
49
115
|
spec.start
|
|
50
116
|
end
|
|
51
117
|
|
|
52
|
-
def
|
|
53
|
-
|
|
118
|
+
def example_failed(name_or_example, *rest)
|
|
119
|
+
@formatter.example_failed(name_or_example, *rest)
|
|
120
|
+
|
|
121
|
+
# In case we fail in before(:all)
|
|
122
|
+
example_started(name_or_example) if @suite.testcases.empty?
|
|
123
|
+
|
|
124
|
+
if name_or_example.respond_to?(:execution_result) # RSpec 2
|
|
125
|
+
failure = RSpec2Failure.new(name_or_example, @formatter)
|
|
126
|
+
else
|
|
127
|
+
failure = RSpecFailure.new(rest[1]) # example_failed(name, counter, failure) in RSpec 1
|
|
128
|
+
end
|
|
129
|
+
|
|
54
130
|
spec = @suite.testcases.last
|
|
55
131
|
spec.finish
|
|
56
|
-
spec.
|
|
132
|
+
spec.name = description_for(name_or_example)
|
|
133
|
+
spec.failures << failure
|
|
57
134
|
end
|
|
58
135
|
|
|
59
|
-
def
|
|
60
|
-
|
|
136
|
+
def example_passed(name_or_example)
|
|
137
|
+
@formatter.example_passed(name_or_example)
|
|
138
|
+
spec = @suite.testcases.last
|
|
139
|
+
spec.finish
|
|
140
|
+
spec.name = description_for(name_or_example)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def example_pending(*args)
|
|
144
|
+
@formatter.example_pending(*args)
|
|
145
|
+
name = description_for(args[0])
|
|
61
146
|
spec = @suite.testcases.last
|
|
62
147
|
spec.finish
|
|
148
|
+
spec.name = "#{name} (PENDING)"
|
|
149
|
+
spec.skipped = true
|
|
63
150
|
end
|
|
64
151
|
|
|
65
152
|
def start_dump
|
|
66
|
-
|
|
153
|
+
@formatter.start_dump
|
|
67
154
|
end
|
|
68
155
|
|
|
69
|
-
def
|
|
70
|
-
|
|
156
|
+
def dump_failures(*args)
|
|
157
|
+
@formatter.dump_failures(*args)
|
|
71
158
|
end
|
|
72
159
|
|
|
73
|
-
def
|
|
74
|
-
|
|
160
|
+
def dump_failure(*args)
|
|
161
|
+
@formatter.dump_failure(*args)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def dump_summary(*args)
|
|
165
|
+
@formatter.dump_summary(*args)
|
|
75
166
|
write_report
|
|
76
167
|
end
|
|
77
168
|
|
|
169
|
+
def dump_pending
|
|
170
|
+
@formatter.dump_pending
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def close
|
|
174
|
+
@formatter.close
|
|
175
|
+
end
|
|
176
|
+
|
|
78
177
|
private
|
|
178
|
+
def description_for(name_or_example)
|
|
179
|
+
if name_or_example.respond_to?(:full_description)
|
|
180
|
+
name_or_example.full_description
|
|
181
|
+
elsif name_or_example.respond_to?(:metadata)
|
|
182
|
+
name_or_example.metadata[:example_group][:full_description]
|
|
183
|
+
elsif name_or_example.respond_to?(:description)
|
|
184
|
+
name_or_example.description
|
|
185
|
+
else
|
|
186
|
+
"UNKNOWN"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
79
190
|
def write_report
|
|
80
191
|
@suite.finish
|
|
81
192
|
@report_manager.write_report(@suite)
|
|
82
193
|
end
|
|
194
|
+
|
|
195
|
+
def new_suite(name)
|
|
196
|
+
write_report if @suite
|
|
197
|
+
@suite = TestSuite.new name
|
|
198
|
+
@suite.start
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
class RSpecDoc < RSpec
|
|
203
|
+
def initialize(*args)
|
|
204
|
+
@formatter = RSpecFormatters::DocFormatter.new(*args)
|
|
205
|
+
super
|
|
206
|
+
end
|
|
83
207
|
end
|
|
84
208
|
end
|
|
85
|
-
end
|
|
209
|
+
end
|
|
@@ -1,39 +1,72 @@
|
|
|
1
|
+
# Copyright (c) 2006-2010 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
|
+
|
|
1
8
|
module CI
|
|
2
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
|
+
|
|
3
38
|
# Basic structure representing the running of a test suite. Used to time tests and store results.
|
|
4
|
-
class TestSuite < Struct.new(:name, :tests, :time, :failures, :errors)
|
|
39
|
+
class TestSuite < Struct.new(:name, :tests, :time, :failures, :errors, :skipped, :assertions)
|
|
5
40
|
attr_accessor :testcases
|
|
41
|
+
attr_accessor :stdout, :stderr
|
|
6
42
|
def initialize(name)
|
|
7
|
-
super
|
|
43
|
+
super(name.to_s) # RSpec passes a "description" object instead of a string
|
|
8
44
|
@testcases = []
|
|
9
45
|
end
|
|
10
46
|
|
|
11
47
|
# Starts timing the test suite.
|
|
12
48
|
def start
|
|
13
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
|
|
14
54
|
end
|
|
15
55
|
|
|
16
56
|
# Finishes timing the test suite.
|
|
17
57
|
def finish
|
|
18
58
|
self.tests = testcases.size
|
|
19
59
|
self.time = Time.now - @start
|
|
20
|
-
self.failures = testcases.
|
|
21
|
-
self.errors = testcases.
|
|
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.skipped = testcases.inject(0) {|sum,tc| sum += (tc.skipped? ? 1 : 0) }
|
|
63
|
+
self.stdout = @capture_out.finish if @capture_out
|
|
64
|
+
self.stderr = @capture_err.finish if @capture_err
|
|
22
65
|
end
|
|
23
66
|
|
|
24
67
|
# Creates the xml builder instance used to create the report xml document.
|
|
25
68
|
def create_builder
|
|
26
|
-
|
|
27
|
-
gem 'builder'
|
|
28
|
-
require 'builder'
|
|
29
|
-
rescue
|
|
30
|
-
begin
|
|
31
|
-
gem 'activesupport'
|
|
32
|
-
require 'active_support'
|
|
33
|
-
rescue
|
|
34
|
-
raise LoadError, "XML Builder is required by CI::Reporter"
|
|
35
|
-
end
|
|
36
|
-
end unless defined?(Builder::XmlMarkup)
|
|
69
|
+
require 'builder'
|
|
37
70
|
# :escape_attrs is obsolete in a newer version, but should do no harm
|
|
38
71
|
Builder::XmlMarkup.new(:indent => 2, :escape_attrs => true)
|
|
39
72
|
end
|
|
@@ -42,29 +75,35 @@ module CI
|
|
|
42
75
|
def to_xml
|
|
43
76
|
builder = create_builder
|
|
44
77
|
# more recent version of Builder doesn't need the escaping
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
txt.sub(/\n.*/m, '...')
|
|
48
|
-
end
|
|
49
|
-
else
|
|
50
|
-
def builder.trunc!(txt)
|
|
51
|
-
_escape(txt.sub(/\n.*/m, '...'))
|
|
52
|
-
end
|
|
78
|
+
def builder.trunc!(txt)
|
|
79
|
+
txt.sub(/\n.*/m, '...')
|
|
53
80
|
end
|
|
54
81
|
builder.instruct!
|
|
55
82
|
attrs = {}
|
|
56
|
-
each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) }
|
|
83
|
+
each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) unless v.nil? || v.to_s.empty? }
|
|
57
84
|
builder.testsuite(attrs) do
|
|
58
85
|
@testcases.each do |tc|
|
|
59
86
|
tc.to_xml(builder)
|
|
60
87
|
end
|
|
88
|
+
builder.tag! "system-out" do
|
|
89
|
+
builder.text! self.stdout
|
|
90
|
+
end
|
|
91
|
+
builder.tag! "system-err" do
|
|
92
|
+
builder.text! self.stderr
|
|
93
|
+
end
|
|
61
94
|
end
|
|
62
95
|
end
|
|
63
96
|
end
|
|
64
97
|
|
|
65
98
|
# Structure used to represent an individual test case. Used to time the test and store the result.
|
|
66
|
-
class TestCase < Struct.new(:name, :time)
|
|
67
|
-
attr_accessor :
|
|
99
|
+
class TestCase < Struct.new(:name, :time, :assertions)
|
|
100
|
+
attr_accessor :failures
|
|
101
|
+
attr_accessor :skipped
|
|
102
|
+
|
|
103
|
+
def initialize(*args)
|
|
104
|
+
super
|
|
105
|
+
@failures = []
|
|
106
|
+
end
|
|
68
107
|
|
|
69
108
|
# Starts timing the test.
|
|
70
109
|
def start
|
|
@@ -78,26 +117,35 @@ module CI
|
|
|
78
117
|
|
|
79
118
|
# Returns non-nil if the test failed.
|
|
80
119
|
def failure?
|
|
81
|
-
|
|
120
|
+
!failures.empty? && failures.detect {|f| f.failure? }
|
|
82
121
|
end
|
|
83
122
|
|
|
84
123
|
# Returns non-nil if the test had an error.
|
|
85
124
|
def error?
|
|
86
|
-
|
|
125
|
+
!failures.empty? && failures.detect {|f| f.error? }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def skipped?
|
|
129
|
+
return skipped
|
|
87
130
|
end
|
|
88
131
|
|
|
89
132
|
# Writes xml representing the test result to the provided builder.
|
|
90
133
|
def to_xml(builder)
|
|
91
134
|
attrs = {}
|
|
92
|
-
each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) }
|
|
135
|
+
each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) unless v.nil? || v.to_s.empty?}
|
|
93
136
|
builder.testcase(attrs) do
|
|
94
|
-
if
|
|
95
|
-
builder.
|
|
96
|
-
|
|
137
|
+
if skipped
|
|
138
|
+
builder.skipped
|
|
139
|
+
else
|
|
140
|
+
failures.each do |failure|
|
|
141
|
+
builder.failure(:type => builder.trunc!(failure.name), :message => builder.trunc!(failure.message)) do
|
|
142
|
+
builder.text!(failure.message + " (#{failure.name})\n")
|
|
143
|
+
builder.text!(failure.location)
|
|
144
|
+
end
|
|
97
145
|
end
|
|
98
146
|
end
|
|
99
147
|
end
|
|
100
148
|
end
|
|
101
149
|
end
|
|
102
150
|
end
|
|
103
|
-
end
|
|
151
|
+
end
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
|
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
|
3
|
+
# software license details.
|
|
4
|
+
|
|
1
5
|
require 'ci/reporter/core'
|
|
2
6
|
require 'test/unit'
|
|
3
7
|
require 'test/unit/ui/console/testrunner'
|
|
@@ -8,15 +12,16 @@ module CI
|
|
|
8
12
|
# of the test.
|
|
9
13
|
class Failure
|
|
10
14
|
def self.new(fault)
|
|
11
|
-
|
|
15
|
+
return TestUnitFailure.new(fault) if fault.kind_of?(Test::Unit::Failure)
|
|
16
|
+
return TestUnitSkipped.new(fault) if Test::Unit.constants.include?("Omission") && (fault.kind_of?(Test::Unit::Omission) || fault.kind_of?(Test::Unit::Pending))
|
|
17
|
+
return TestUnitNotification.new(fault) if Test::Unit.constants.include?("Notification") && fault.kind_of?(Test::Unit::Notification)
|
|
18
|
+
TestUnitError.new(fault)
|
|
12
19
|
end
|
|
13
20
|
end
|
|
14
21
|
|
|
15
22
|
# Wrapper around a <code>Test::Unit</code> error to be used by the test suite to interpret results.
|
|
16
23
|
class TestUnitError
|
|
17
|
-
def initialize(fault)
|
|
18
|
-
@fault = fault
|
|
19
|
-
end
|
|
24
|
+
def initialize(fault) @fault = fault end
|
|
20
25
|
def failure?() false end
|
|
21
26
|
def error?() true end
|
|
22
27
|
def name() @fault.exception.class.name end
|
|
@@ -26,9 +31,7 @@ module CI
|
|
|
26
31
|
|
|
27
32
|
# Wrapper around a <code>Test::Unit</code> failure to be used by the test suite to interpret results.
|
|
28
33
|
class TestUnitFailure
|
|
29
|
-
def initialize(fault)
|
|
30
|
-
@fault = fault
|
|
31
|
-
end
|
|
34
|
+
def initialize(fault) @fault = fault end
|
|
32
35
|
def failure?() true end
|
|
33
36
|
def error?() false end
|
|
34
37
|
def name() Test::Unit::AssertionFailedError.name end
|
|
@@ -36,6 +39,26 @@ module CI
|
|
|
36
39
|
def location() @fault.location.join("\n") end
|
|
37
40
|
end
|
|
38
41
|
|
|
42
|
+
# Wrapper around a <code>Test::Unit</code> 2.0 omission.
|
|
43
|
+
class TestUnitSkipped
|
|
44
|
+
def initialize(fault) @fault = fault end
|
|
45
|
+
def failure?() false end
|
|
46
|
+
def error?() false end
|
|
47
|
+
def name() Test::Unit::Omission.name end
|
|
48
|
+
def message() @fault.message end
|
|
49
|
+
def location() @fault.location.join("\n") end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Wrapper around a <code>Test::Unit</code> 2.0 notification.
|
|
53
|
+
class TestUnitNotification
|
|
54
|
+
def initialize(fault) @fault = fault end
|
|
55
|
+
def failure?() false end
|
|
56
|
+
def error?() false end
|
|
57
|
+
def name() Test::Unit::Notification.name end
|
|
58
|
+
def message() @fault.message end
|
|
59
|
+
def location() @fault.location.join("\n") end
|
|
60
|
+
end
|
|
61
|
+
|
|
39
62
|
# Replacement Mediator that adds listeners to capture the results of the <code>Test::Unit</code> runs.
|
|
40
63
|
class TestUnit < Test::Unit::UI::TestRunnerMediator
|
|
41
64
|
def initialize(suite, report_mgr = nil)
|
|
@@ -49,8 +72,11 @@ module CI
|
|
|
49
72
|
end
|
|
50
73
|
|
|
51
74
|
def started(result)
|
|
75
|
+
@suite_result = result
|
|
76
|
+
@last_assertion_count = 0
|
|
52
77
|
@current_suite = nil
|
|
53
78
|
@unknown_count = 0
|
|
79
|
+
@result_assertion_count = 0
|
|
54
80
|
end
|
|
55
81
|
|
|
56
82
|
def test_started(name)
|
|
@@ -67,7 +93,8 @@ module CI
|
|
|
67
93
|
end
|
|
68
94
|
|
|
69
95
|
def fault(fault)
|
|
70
|
-
|
|
96
|
+
tc = @current_suite.testcases.last
|
|
97
|
+
tc.failures << Failure.new(fault)
|
|
71
98
|
end
|
|
72
99
|
|
|
73
100
|
def finished(elapsed_time)
|
|
@@ -92,7 +119,9 @@ module CI
|
|
|
92
119
|
|
|
93
120
|
def finish_suite
|
|
94
121
|
if @current_suite
|
|
95
|
-
@current_suite.finish
|
|
122
|
+
@current_suite.finish
|
|
123
|
+
@current_suite.assertions = @suite_result.assertion_count - @last_assertion_count
|
|
124
|
+
@last_assertion_count = @suite_result.assertion_count
|
|
96
125
|
@report_manager.write_report(@current_suite)
|
|
97
126
|
end
|
|
98
127
|
end
|
|
@@ -103,11 +132,12 @@ module CI
|
|
|
103
132
|
@current_suite.testcases << tc
|
|
104
133
|
end
|
|
105
134
|
|
|
106
|
-
def finish_test
|
|
135
|
+
def finish_test
|
|
107
136
|
tc = @current_suite.testcases.last
|
|
108
137
|
tc.finish
|
|
109
|
-
tc.
|
|
138
|
+
tc.assertions = @suite_result.assertion_count - @result_assertion_count
|
|
139
|
+
@result_assertion_count = @suite_result.assertion_count
|
|
110
140
|
end
|
|
111
141
|
end
|
|
112
142
|
end
|
|
113
|
-
end
|
|
143
|
+
end
|