stopdropandrew_ci_reporter 1.7.0.1
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 +166 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +32 -0
- data/README.rdoc +67 -0
- data/Rakefile +120 -0
- data/lib/ci/reporter/core.rb +6 -0
- data/lib/ci/reporter/cucumber.rb +125 -0
- data/lib/ci/reporter/minitest.rb +224 -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/minitest.rb +15 -0
- data/lib/ci/reporter/rake/minitest_loader.rb +9 -0
- data/lib/ci/reporter/rake/rspec.rb +31 -0
- data/lib/ci/reporter/rake/rspec_loader.rb +6 -0
- data/lib/ci/reporter/rake/test_unit.rb +15 -0
- data/lib/ci/reporter/rake/test_unit_loader.rb +36 -0
- data/lib/ci/reporter/rake/utils.rb +14 -0
- data/lib/ci/reporter/report_manager.rb +63 -0
- data/lib/ci/reporter/rspec.rb +227 -0
- data/lib/ci/reporter/test_suite.rb +156 -0
- data/lib/ci/reporter/test_unit.rb +143 -0
- data/lib/ci/reporter/version.rb +11 -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 +105 -0
- data/spec/ci/reporter/report_manager_spec.rb +62 -0
- data/spec/ci/reporter/rspec_spec.rb +152 -0
- data/spec/ci/reporter/test_suite_spec.rb +160 -0
- data/spec/ci/reporter/test_unit_spec.rb +152 -0
- data/spec/spec_helper.rb +23 -0
- data/stub.rake +15 -0
- data/tasks/ci_reporter.rake +20 -0
- metadata +173 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
# Copyright (c) 2012 Alexander Shcherbinin <alexander.shcherbinin@gmail.com>
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
3
|
+
# software license details.
|
4
|
+
|
5
|
+
require 'ci/reporter/core'
|
6
|
+
|
7
|
+
require 'minitest/unit'
|
8
|
+
|
9
|
+
module CI
|
10
|
+
module Reporter
|
11
|
+
class Failure
|
12
|
+
def self.new(fault, type = nil, meth = nil)
|
13
|
+
return MiniTestSkipped.new(fault) if type == :skip
|
14
|
+
return MiniTestFailure.new(fault, meth) if type == :failure
|
15
|
+
MiniTestError.new(fault)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class FailureCore
|
20
|
+
def location(e)
|
21
|
+
last_before_assertion = ""
|
22
|
+
e.backtrace.reverse_each do |s|
|
23
|
+
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
|
24
|
+
last_before_assertion = s
|
25
|
+
end
|
26
|
+
last_before_assertion.sub(/:in .*$/, '')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class MiniTestSkipped < FailureCore
|
31
|
+
def initialize(fault) @fault = fault end
|
32
|
+
def failure?() false end
|
33
|
+
def error?() false end
|
34
|
+
def name() @fault.class.name end
|
35
|
+
def message() @fault.message end
|
36
|
+
def location() super @fault end
|
37
|
+
end
|
38
|
+
|
39
|
+
class MiniTestFailure < FailureCore
|
40
|
+
def initialize(fault, meth) @fault = fault; @meth = meth end
|
41
|
+
def failure?() true end
|
42
|
+
def error?() false end
|
43
|
+
def name() @meth end
|
44
|
+
def message() @fault.message end
|
45
|
+
def location() super @fault end
|
46
|
+
end
|
47
|
+
|
48
|
+
class MiniTestError < FailureCore
|
49
|
+
def initialize(fault) @fault = fault end
|
50
|
+
def failure?() false end
|
51
|
+
def error?() true end
|
52
|
+
def name() @fault.exception.class.name end
|
53
|
+
def message() @fault.exception.message end
|
54
|
+
def location() @fault.exception.backtrace.join("\n") end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Runner < MiniTest::Unit
|
58
|
+
|
59
|
+
@@out = $stdout
|
60
|
+
|
61
|
+
def initialize
|
62
|
+
super
|
63
|
+
@report_manager = ReportManager.new("test")
|
64
|
+
end
|
65
|
+
|
66
|
+
def _run_anything(type)
|
67
|
+
suites = MiniTest::Unit::TestCase.send "#{type}_suites"
|
68
|
+
return if suites.empty?
|
69
|
+
|
70
|
+
started_anything type
|
71
|
+
|
72
|
+
sync = output.respond_to? :"sync=" # stupid emacs
|
73
|
+
old_sync, output.sync = output.sync, true if sync
|
74
|
+
|
75
|
+
_run_suites(suites, type)
|
76
|
+
|
77
|
+
output.sync = old_sync if sync
|
78
|
+
|
79
|
+
finished_anything(type)
|
80
|
+
end
|
81
|
+
|
82
|
+
def _run_suites(suites, type)
|
83
|
+
suites.map { |suite| _run_suite suite, type }
|
84
|
+
end
|
85
|
+
|
86
|
+
def _run_suite(suite, type)
|
87
|
+
start_suite(suite)
|
88
|
+
|
89
|
+
header = "#{type}_suite_header"
|
90
|
+
puts send(header, suite) if respond_to? header
|
91
|
+
|
92
|
+
filter_suite_methods(suite, type).each do |method|
|
93
|
+
_run_test(suite, method)
|
94
|
+
end
|
95
|
+
|
96
|
+
finish_suite
|
97
|
+
end
|
98
|
+
|
99
|
+
def _run_test(suite, method)
|
100
|
+
start_case(method)
|
101
|
+
|
102
|
+
result = run_test(suite, method)
|
103
|
+
|
104
|
+
@assertion_count += result._assertions
|
105
|
+
@test_count += 1
|
106
|
+
|
107
|
+
finish_case
|
108
|
+
end
|
109
|
+
|
110
|
+
def puke(klass, meth, e)
|
111
|
+
e = case e
|
112
|
+
when MiniTest::Skip then
|
113
|
+
@skips += 1
|
114
|
+
fault(e, :skip)
|
115
|
+
return "S" unless @verbose
|
116
|
+
"Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
|
117
|
+
when MiniTest::Assertion then
|
118
|
+
@failures += 1
|
119
|
+
fault(e, :failure, meth)
|
120
|
+
"Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
|
121
|
+
else
|
122
|
+
@errors += 1
|
123
|
+
fault(e, :error)
|
124
|
+
bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
|
125
|
+
"Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
|
126
|
+
end
|
127
|
+
@report << e
|
128
|
+
e[0, 1]
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def started_anything(type)
|
134
|
+
@test_count = 0
|
135
|
+
@assertion_count = 0
|
136
|
+
@last_assertion_count = 0
|
137
|
+
@result_assertion_count = 0
|
138
|
+
@start = Time.now
|
139
|
+
|
140
|
+
puts
|
141
|
+
puts "# Running #{type}s:"
|
142
|
+
puts
|
143
|
+
end
|
144
|
+
|
145
|
+
def finished_anything(type)
|
146
|
+
t = Time.now - @start
|
147
|
+
puts
|
148
|
+
puts
|
149
|
+
puts "Finished #{type}s in %.6fs, %.4f tests/s, %.4f assertions/s." %
|
150
|
+
[t, @test_count / t, @assertion_count / t]
|
151
|
+
|
152
|
+
report.each_with_index do |msg, i|
|
153
|
+
puts "\n%3d) %s" % [i + 1, msg]
|
154
|
+
end
|
155
|
+
|
156
|
+
puts
|
157
|
+
|
158
|
+
status
|
159
|
+
end
|
160
|
+
|
161
|
+
def filter_suite_methods(suite, type)
|
162
|
+
filter = options[:filter] || '/./'
|
163
|
+
filter = Regexp.new $1 if filter =~ /\/(.*)\//
|
164
|
+
|
165
|
+
suite.send("#{type}_methods").grep(filter)
|
166
|
+
end
|
167
|
+
|
168
|
+
def run_test(suite, method)
|
169
|
+
inst = suite.new method
|
170
|
+
inst._assertions = 0
|
171
|
+
|
172
|
+
print "#{suite}##{method} = " if @verbose
|
173
|
+
|
174
|
+
@start_time = Time.now
|
175
|
+
result = inst.run self
|
176
|
+
time = Time.now - @start_time
|
177
|
+
|
178
|
+
print "%.2f s = " % time if @verbose
|
179
|
+
print result
|
180
|
+
puts if @verbose
|
181
|
+
|
182
|
+
return inst
|
183
|
+
end
|
184
|
+
|
185
|
+
def start_suite(suite_name)
|
186
|
+
@current_suite = CI::Reporter::TestSuite.new(suite_name)
|
187
|
+
@current_suite.start
|
188
|
+
end
|
189
|
+
|
190
|
+
def finish_suite
|
191
|
+
if @current_suite
|
192
|
+
@current_suite.finish
|
193
|
+
@current_suite.assertions = @assertion_count - @last_assertion_count
|
194
|
+
@last_assertion_count = @assertion_count
|
195
|
+
@report_manager.write_report(@current_suite)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def start_case(test_name)
|
200
|
+
tc = CI::Reporter::TestCase.new(test_name)
|
201
|
+
tc.start
|
202
|
+
@current_suite.testcases << tc
|
203
|
+
end
|
204
|
+
|
205
|
+
def finish_case
|
206
|
+
tc = @current_suite.testcases.last
|
207
|
+
tc.finish
|
208
|
+
tc.assertions = @assertion_count - @result_assertion_count
|
209
|
+
@result_assertion_count = @assertion_count
|
210
|
+
end
|
211
|
+
|
212
|
+
def fault(fault, type = nil, meth = nil)
|
213
|
+
tc = @current_suite.testcases.last
|
214
|
+
if :skip == type
|
215
|
+
tc.skipped = true
|
216
|
+
else
|
217
|
+
tc.failures << Failure.new(fault, type, meth)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright (c) 2006-2012 Nick Sieger <nicksieger@gmail.com>
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
3
|
+
# software license details.
|
4
|
+
|
5
|
+
require File.expand_path('../utils', __FILE__)
|
6
|
+
|
7
|
+
namespace :ci do
|
8
|
+
namespace :setup do
|
9
|
+
task :cucumber_report_cleanup do
|
10
|
+
rm_rf ENV["CI_REPORTS"] || "features/reports"
|
11
|
+
end
|
12
|
+
|
13
|
+
task :cucumber => :cucumber_report_cleanup do
|
14
|
+
cuke_opts = ["--require", CI::Reporter.maybe_quote_filename("#{File.dirname(__FILE__)}/cucumber_loader.rb"),
|
15
|
+
"--format", "CI::Reporter::Cucumber"].join(" ")
|
16
|
+
ENV["CUCUMBER_OPTS"] = "#{ENV['CUCUMBER_OPTS']} #{cuke_opts}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Copyright (c) 2012 Alexander Shcherbinin <alexander.shcherbinin@gmail.com>
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
3
|
+
# software license details.
|
4
|
+
|
5
|
+
require File.expand_path('../utils', __FILE__)
|
6
|
+
|
7
|
+
namespace :ci do
|
8
|
+
namespace :setup do
|
9
|
+
task :minitest do
|
10
|
+
rm_rf ENV["CI_REPORTS"] || "test/reports"
|
11
|
+
test_loader = CI::Reporter.maybe_quote_filename "#{File.dirname(__FILE__)}/minitest_loader.rb"
|
12
|
+
ENV["TESTOPTS"] = "#{ENV["TESTOPTS"]} #{test_loader}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Copyright (c) 2012 Alexander Shcherbinin <alexander.shcherbinin@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/minitest'
|
7
|
+
|
8
|
+
# set defaults
|
9
|
+
MiniTest::Unit.runner = CI::Reporter::Runner.new
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copyright (c) 2006-2012 Nick Sieger <nicksieger@gmail.com>
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
3
|
+
# software license details.
|
4
|
+
|
5
|
+
require File.expand_path('../utils', __FILE__)
|
6
|
+
|
7
|
+
namespace :ci do
|
8
|
+
namespace :setup do
|
9
|
+
task :spec_report_cleanup do
|
10
|
+
rm_rf ENV["CI_REPORTS"] || "spec/reports"
|
11
|
+
end
|
12
|
+
|
13
|
+
task :rspec => :spec_report_cleanup do
|
14
|
+
spec_opts = ["--require", CI::Reporter.maybe_quote_filename("#{File.dirname(__FILE__)}/rspec_loader.rb"),
|
15
|
+
"--format", "CI::Reporter::RSpec"].join(" ")
|
16
|
+
ENV["SPEC_OPTS"] = "#{ENV['SPEC_OPTS']} #{spec_opts}"
|
17
|
+
end
|
18
|
+
|
19
|
+
task :rspecdoc => :spec_report_cleanup do
|
20
|
+
spec_opts = ["--require", CI::Reporter.maybe_quote_filename("#{File.dirname(__FILE__)}/rspec_loader.rb"),
|
21
|
+
"--format", "CI::Reporter::RSpecDoc"].join(" ")
|
22
|
+
ENV["SPEC_OPTS"] = "#{ENV['SPEC_OPTS']} #{spec_opts}"
|
23
|
+
end
|
24
|
+
|
25
|
+
task :rspecbase => :spec_report_cleanup do
|
26
|
+
spec_opts = ["--require", CI::Reporter.maybe_quote_filename("#{File.dirname(__FILE__)}/rspec_loader.rb"),
|
27
|
+
"--format", "CI::Reporter::RSpecBase"].join(" ")
|
28
|
+
ENV["SPEC_OPTS"] = "#{ENV['SPEC_OPTS']} #{spec_opts}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Copyright (c) 2006-2012 Nick Sieger <nicksieger@gmail.com>
|
2
|
+
# See the file LICENSE.txt included with the distribution for
|
3
|
+
# software license details.
|
4
|
+
|
5
|
+
require File.expand_path('../utils', __FILE__)
|
6
|
+
|
7
|
+
namespace :ci do
|
8
|
+
namespace :setup do
|
9
|
+
task :testunit do
|
10
|
+
rm_rf ENV["CI_REPORTS"] || "test/reports"
|
11
|
+
test_loader = CI::Reporter.maybe_quote_filename "#{File.dirname(__FILE__)}/test_unit_loader.rb"
|
12
|
+
ENV["TESTOPTS"] = "#{ENV["TESTOPTS"]} #{test_loader}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright (c) 2006-2012 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
|
+
# Intercepts mediator creation in ruby-test < 2.1
|
9
|
+
module Test #:nodoc:all
|
10
|
+
module Unit
|
11
|
+
module UI
|
12
|
+
module Console
|
13
|
+
class TestRunner
|
14
|
+
def create_mediator(suite)
|
15
|
+
# swap in our custom mediator
|
16
|
+
return CI::Reporter::TestUnit.new(suite)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Intercepts mediator creation in ruby-test >= 2.1
|
25
|
+
module Test #:nodoc:all
|
26
|
+
module Unit
|
27
|
+
module UI
|
28
|
+
class TestRunner
|
29
|
+
def setup_mediator
|
30
|
+
# swap in our custom mediator
|
31
|
+
@mediator = CI::Reporter::TestUnit.new(@suite)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Copyright (c) 2006-2012 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
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Copyright (c) 2006-2012 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(filename_for(suite), "w") do |f|
|
18
|
+
f << suite.to_xml
|
19
|
+
end
|
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
|
+
MAX_FILENAME_SIZE = 240
|
38
|
+
#
|
39
|
+
def filename_for(suite)
|
40
|
+
basename = "#{@basename}-#{suite.name.gsub(/[^a-zA-Z0-9]+/, '-')}"
|
41
|
+
suffix = "xml"
|
42
|
+
|
43
|
+
# shorten basename if it exceeds 240 characters
|
44
|
+
# most filesystems have a 255 character limit
|
45
|
+
# so leave some room for the sidesteps
|
46
|
+
basename = basename[0..MAX_FILENAME_SIZE] if basename.length > MAX_FILENAME_SIZE
|
47
|
+
|
48
|
+
# the initial filename, e.g. SPEC-MailsController.xml
|
49
|
+
filename = [basename, suffix].join(".")
|
50
|
+
|
51
|
+
# if the initial filename is already in use
|
52
|
+
# do sidesteps, beginning with SPEC-MailsController.0.xml
|
53
|
+
i = 0
|
54
|
+
while File.exists?(filename) && i < MAX_SIDESTEPS
|
55
|
+
filename = [basename, i, suffix].join(".")
|
56
|
+
i += 1
|
57
|
+
end
|
58
|
+
|
59
|
+
filename
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|