ci_reporter 1.0
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 +3 -0
- data/Manifest.txt +19 -0
- data/README.txt +45 -0
- data/Rakefile +34 -0
- data/lib/ci/reporter/core.rb +2 -0
- data/lib/ci/reporter/rake/rspec.rb +9 -0
- data/lib/ci/reporter/rake/rspec_loader.rb +7 -0
- data/lib/ci/reporter/rake/test_unit.rb +8 -0
- data/lib/ci/reporter/rake/test_unit_loader.rb +22 -0
- data/lib/ci/reporter/report_manager.rb +19 -0
- data/lib/ci/reporter/rspec.rb +85 -0
- data/lib/ci/reporter/test_suite.rb +103 -0
- data/lib/ci/reporter/test_unit.rb +113 -0
- data/spec/ci/reporter/report_manager_spec.rb +35 -0
- data/spec/ci/reporter/rspec_spec.rb +35 -0
- data/spec/ci/reporter/test_suite_spec.rb +116 -0
- data/spec/ci/reporter/test_unit_spec.rb +75 -0
- data/spec/spec_helper.rb +9 -0
- data/tasks/ci_reporter.rake +12 -0
- metadata +67 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/ci/reporter/core.rb
|
6
|
+
lib/ci/reporter/report_manager.rb
|
7
|
+
lib/ci/reporter/rspec.rb
|
8
|
+
lib/ci/reporter/test_suite.rb
|
9
|
+
lib/ci/reporter/test_unit.rb
|
10
|
+
lib/ci/reporter/rake/rspec.rb
|
11
|
+
lib/ci/reporter/rake/rspec_loader.rb
|
12
|
+
lib/ci/reporter/rake/test_unit.rb
|
13
|
+
lib/ci/reporter/rake/test_unit_loader.rb
|
14
|
+
spec/spec_helper.rb
|
15
|
+
spec/ci/reporter/report_manager_spec.rb
|
16
|
+
spec/ci/reporter/rspec_spec.rb
|
17
|
+
spec/ci/reporter/test_suite_spec.rb
|
18
|
+
spec/ci/reporter/test_unit_spec.rb
|
19
|
+
tasks/ci_reporter.rake
|
data/README.txt
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
CI::Reporter is an add-on to Test::Unit and RSpec that allows you to generate
|
2
|
+
XML reports of your test and/or spec runs. The resulting files can be read by
|
3
|
+
a continuous integration system that understands Ant's JUnit report XML
|
4
|
+
format, thus allowing your CI system to track test/spec successes and
|
5
|
+
failures.
|
6
|
+
|
7
|
+
== Dependencies
|
8
|
+
|
9
|
+
CI::Reporter has one required dependency on Builder, but since many will have a viable version of Builder via Rails' ActiveSupport gem, Builder is not a direct dependency of the project at the moment. Instead, ensure that you have either the +builder+ or +activesupport+ gem installed before continuing.
|
10
|
+
|
11
|
+
== Installation
|
12
|
+
|
13
|
+
CI::Reporter is available as a gem. To install the gem, use the usual gem command:
|
14
|
+
|
15
|
+
gem install ci_reporter
|
16
|
+
|
17
|
+
To use CI::Reporter as a Rails plugin, first install the gem, and then install the plugin as follows:
|
18
|
+
|
19
|
+
script/plugin install http://svn.caldersphere.net/svn/main/plugins/ci_reporter
|
20
|
+
|
21
|
+
== Usage
|
22
|
+
|
23
|
+
CI::Reporter works best with projects that use a +Rakefile+ along with the standard <code>Rake::TestTask</code> or <code>Spec::Rake::SpecTask</code> tasks for running tests or specs, respectively. In this fashion, it hooks into <code>Test::Unit</code> or +RSpec+ using environment variables recognized by these custom tasks to inject the CI::Reporter code into the test or spec runs. If you're using the Rails plugin, step 1 is unnecessary; skip to step 2.
|
24
|
+
|
25
|
+
1. To use CI::Reporter, simply add the following lines to your Rakefile:
|
26
|
+
|
27
|
+
require 'rubygems'
|
28
|
+
gem 'ci_reporter'
|
29
|
+
require 'ci/reporter/rake/rspec' # use this if you're using RSpec
|
30
|
+
require 'ci/reporter/rake/test_unit' # use this if you're using Test::Unit
|
31
|
+
|
32
|
+
2. Next, either modify your Rakefile to make the <code>ci:setup:rspec</code> or <code>ci:setup:testunit</code> task a dependency of your test tasks, or include them on the Rake command-line before the name of the task that runs the tests or specs.
|
33
|
+
|
34
|
+
rake ci:setup:testunit test
|
35
|
+
|
36
|
+
== Advanced Usage
|
37
|
+
|
38
|
+
If for some reason you can't use the above technique to inject CI::Reporter, you'll have to do one of these:
|
39
|
+
|
40
|
+
1. If you're using <code>Test::Unit</code>, ensure the <code>ci/reporter/rake/test_unit_loader.rb</code> file is loaded or required at some point before the tests are run.
|
41
|
+
2. If you're using +RSpec+, you'll need to pass the following arguments to the +spec+ command:
|
42
|
+
--require GEM_PATH/lib/ci/reporter/rake/rspec_loader
|
43
|
+
--format CI::Reporter::RSpec
|
44
|
+
|
45
|
+
There's a bit of a chicken and egg problem because rubygems needs to be loaded before you can require any CI::Reporter files. If you cringe hard-coding a full path to a specific version of the gem, you can also copy the +rspec_loader+ file into your project and require it directly -- the contents are version-agnostic and are not likely to change in future releases.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
require 'hoe'
|
3
|
+
|
4
|
+
MANIFEST = FileList["History.txt", "Manifest.txt", "README.txt", "Rakefile",
|
5
|
+
"lib/**/*.rb", "spec/**/*.rb", "tasks/**/*.rake"]
|
6
|
+
|
7
|
+
Hoe.new("ci_reporter", "1.0") do |p|
|
8
|
+
p.rubyforge_name = "caldersphere"
|
9
|
+
p.url = "http://caldersphere.rubyforge.org/ci_reporter"
|
10
|
+
p.author = "Nick Sieger"
|
11
|
+
p.email = "nick@nicksieger.com"
|
12
|
+
p.summary = "CI::Reporter allows you to generate reams of XML for use with continuous integration systems."
|
13
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
14
|
+
p.description = p.paragraphs_of('README.txt', 0...1).join("\n\n")
|
15
|
+
p.extra_deps.reject!{|d| d.first == "hoe"}
|
16
|
+
p.test_globs = ["spec/**/*_spec.rb"]
|
17
|
+
end.spec.files = MANIFEST
|
18
|
+
|
19
|
+
# Hoe insists on setting task :default => :test
|
20
|
+
# !@#$ no easy way to empty the default list of prerequisites
|
21
|
+
Rake::Task['default'].send :instance_variable_set, "@prerequisites", FileList[]
|
22
|
+
|
23
|
+
task :default => :spec
|
24
|
+
|
25
|
+
Spec::Rake::SpecTask.new do |t|
|
26
|
+
t.spec_opts = ["--diff", "unified"]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Automated manifest
|
30
|
+
task :manifest do
|
31
|
+
File.open("Manifest.txt", "w") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
|
32
|
+
end
|
33
|
+
|
34
|
+
task :package => :manifest
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin
|
3
|
+
gem 'ci_reporter'
|
4
|
+
rescue
|
5
|
+
$: << File.dirname(__FILE__) + "/../../../lib"
|
6
|
+
end
|
7
|
+
require 'ci/reporter/test_unit'
|
8
|
+
|
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
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module CI #:nodoc:
|
4
|
+
module Reporter #:nodoc:
|
5
|
+
class ReportManager
|
6
|
+
def initialize(prefix)
|
7
|
+
@basedir = ENV['CI_REPORTS'] || File.expand_path("#{Dir.getwd}/#{prefix.downcase}/reports")
|
8
|
+
@basename = "#{@basedir}/#{prefix.upcase}"
|
9
|
+
FileUtils.mkdir_p(@basedir)
|
10
|
+
end
|
11
|
+
|
12
|
+
def write_report(suite)
|
13
|
+
File.open("#{@basename}-#{suite.name.gsub(/[^a-zA-Z0-9]+/, '-')}.xml", "w") do |f|
|
14
|
+
f << suite.to_xml
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'ci/reporter/core'
|
2
|
+
gem 'rspec'
|
3
|
+
require 'spec'
|
4
|
+
|
5
|
+
module CI
|
6
|
+
module Reporter
|
7
|
+
# Wrapper around a <code>RSpec</code> error or failure to be used by the test suite to interpret results.
|
8
|
+
class RSpecFailure
|
9
|
+
def initialize(failure)
|
10
|
+
@failure = failure
|
11
|
+
end
|
12
|
+
|
13
|
+
def failure?
|
14
|
+
@failure.expectation_not_met?
|
15
|
+
end
|
16
|
+
|
17
|
+
def error?
|
18
|
+
!@failure.expectation_not_met?
|
19
|
+
end
|
20
|
+
|
21
|
+
def name() @failure.exception.class.name end
|
22
|
+
def message() @failure.exception.message end
|
23
|
+
def location() @failure.exception.backtrace.join("\n") end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Custom +RSpec+ formatter used to hook into the spec runs and capture results.
|
27
|
+
class RSpec < Spec::Runner::Formatter::ProgressBarFormatter
|
28
|
+
def initialize(output, dry_run=false, colour=false, report_mgr=nil)
|
29
|
+
super(output, dry_run, colour)
|
30
|
+
@report_manager = report_mgr || ReportManager.new("spec")
|
31
|
+
@suite = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def start(spec_count)
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_context(name, first)
|
39
|
+
super
|
40
|
+
write_report if @suite
|
41
|
+
@suite = TestSuite.new name
|
42
|
+
@suite.start
|
43
|
+
end
|
44
|
+
|
45
|
+
def spec_started(name)
|
46
|
+
super
|
47
|
+
spec = TestCase.new name
|
48
|
+
@suite.testcases << spec
|
49
|
+
spec.start
|
50
|
+
end
|
51
|
+
|
52
|
+
def spec_failed(name, counter, failure)
|
53
|
+
super
|
54
|
+
spec = @suite.testcases.last
|
55
|
+
spec.finish
|
56
|
+
spec.failure = RSpecFailure.new(failure)
|
57
|
+
end
|
58
|
+
|
59
|
+
def spec_passed(name)
|
60
|
+
super
|
61
|
+
spec = @suite.testcases.last
|
62
|
+
spec.finish
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_dump
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
69
|
+
def dump_failure(counter, failure)
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
def dump_summary(duration, spec_count, failure_count)
|
74
|
+
super
|
75
|
+
write_report
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def write_report
|
80
|
+
@suite.finish
|
81
|
+
@report_manager.write_report(@suite)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module CI
|
2
|
+
module Reporter
|
3
|
+
# 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)
|
5
|
+
attr_accessor :testcases
|
6
|
+
def initialize(name)
|
7
|
+
super
|
8
|
+
@testcases = []
|
9
|
+
end
|
10
|
+
|
11
|
+
# Starts timing the test suite.
|
12
|
+
def start
|
13
|
+
@start = Time.now
|
14
|
+
end
|
15
|
+
|
16
|
+
# Finishes timing the test suite.
|
17
|
+
def finish
|
18
|
+
self.tests = testcases.size
|
19
|
+
self.time = Time.now - @start
|
20
|
+
self.failures = testcases.select {|tc| tc.failure? }.size
|
21
|
+
self.errors = testcases.select {|tc| tc.error? }.size
|
22
|
+
end
|
23
|
+
|
24
|
+
# Creates the xml builder instance used to create the report xml document.
|
25
|
+
def create_builder
|
26
|
+
begin
|
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)
|
37
|
+
# :escape_attrs is obsolete in a newer version, but should do no harm
|
38
|
+
Builder::XmlMarkup.new(:indent => 2, :escape_attrs => true)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Creates an xml string containing the test suite results.
|
42
|
+
def to_xml
|
43
|
+
builder = create_builder
|
44
|
+
# more recent version of Builder doesn't need the escaping
|
45
|
+
if Builder::XmlMarkup.private_instance_methods.include?("_attr_value")
|
46
|
+
def builder.trunc!(txt)
|
47
|
+
txt.sub(/\n.*/m, '...')
|
48
|
+
end
|
49
|
+
else
|
50
|
+
def builder.trunc!(txt)
|
51
|
+
_escape(txt.sub(/\n.*/m, '...'))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
builder.instruct!
|
55
|
+
attrs = {}
|
56
|
+
each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) }
|
57
|
+
builder.testsuite(attrs) do
|
58
|
+
@testcases.each do |tc|
|
59
|
+
tc.to_xml(builder)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# 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 :failure
|
68
|
+
|
69
|
+
# Starts timing the test.
|
70
|
+
def start
|
71
|
+
@start = Time.now
|
72
|
+
end
|
73
|
+
|
74
|
+
# Finishes timing the test.
|
75
|
+
def finish
|
76
|
+
self.time = Time.now - @start
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns non-nil if the test failed.
|
80
|
+
def failure?
|
81
|
+
failure && failure.failure?
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns non-nil if the test had an error.
|
85
|
+
def error?
|
86
|
+
failure && failure.error?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Writes xml representing the test result to the provided builder.
|
90
|
+
def to_xml(builder)
|
91
|
+
attrs = {}
|
92
|
+
each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) }
|
93
|
+
builder.testcase(attrs) do
|
94
|
+
if failure
|
95
|
+
builder.failure(:type => builder.trunc!(failure.name), :message => builder.trunc!(failure.message)) do
|
96
|
+
builder.text!(failure.location)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'ci/reporter/core'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'test/unit/ui/console/testrunner'
|
4
|
+
|
5
|
+
module CI
|
6
|
+
module Reporter
|
7
|
+
# Factory for constructing either a CI::Reporter::TestUnitFailure or CI::Reporter::TestUnitError depending on the result
|
8
|
+
# of the test.
|
9
|
+
class Failure
|
10
|
+
def self.new(fault)
|
11
|
+
fault.kind_of?(Test::Unit::Failure) ? TestUnitFailure.new(fault) : TestUnitError.new(fault)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Wrapper around a <code>Test::Unit</code> error to be used by the test suite to interpret results.
|
16
|
+
class TestUnitError
|
17
|
+
def initialize(fault)
|
18
|
+
@fault = fault
|
19
|
+
end
|
20
|
+
def failure?() false end
|
21
|
+
def error?() true end
|
22
|
+
def name() @fault.exception.class.name end
|
23
|
+
def message() @fault.exception.message end
|
24
|
+
def location() @fault.exception.backtrace.join("\n") end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Wrapper around a <code>Test::Unit</code> failure to be used by the test suite to interpret results.
|
28
|
+
class TestUnitFailure
|
29
|
+
def initialize(fault)
|
30
|
+
@fault = fault
|
31
|
+
end
|
32
|
+
def failure?() true end
|
33
|
+
def error?() false end
|
34
|
+
def name() Test::Unit::AssertionFailedError.name end
|
35
|
+
def message() @fault.message end
|
36
|
+
def location() @fault.location.join("\n") end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Replacement Mediator that adds listeners to capture the results of the <code>Test::Unit</code> runs.
|
40
|
+
class TestUnit < Test::Unit::UI::TestRunnerMediator
|
41
|
+
def initialize(suite, report_mgr = nil)
|
42
|
+
super(suite)
|
43
|
+
@report_manager = report_mgr || ReportManager.new("test")
|
44
|
+
add_listener(Test::Unit::UI::TestRunnerMediator::STARTED, &method(:started))
|
45
|
+
add_listener(Test::Unit::TestCase::STARTED, &method(:test_started))
|
46
|
+
add_listener(Test::Unit::TestCase::FINISHED, &method(:test_finished))
|
47
|
+
add_listener(Test::Unit::TestResult::FAULT, &method(:fault))
|
48
|
+
add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED, &method(:finished))
|
49
|
+
end
|
50
|
+
|
51
|
+
def started(result)
|
52
|
+
@current_suite = nil
|
53
|
+
@unknown_count = 0
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_started(name)
|
57
|
+
test_name, suite_name = extract_names(name)
|
58
|
+
unless @current_suite && @current_suite.name == suite_name
|
59
|
+
finish_suite
|
60
|
+
start_suite(suite_name)
|
61
|
+
end
|
62
|
+
start_test(test_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_finished(name)
|
66
|
+
finish_test
|
67
|
+
end
|
68
|
+
|
69
|
+
def fault(fault)
|
70
|
+
finish_test(fault)
|
71
|
+
end
|
72
|
+
|
73
|
+
def finished(elapsed_time)
|
74
|
+
finish_suite
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def extract_names(name)
|
79
|
+
match = name.match(/(.*)\(([^)]*)\)/)
|
80
|
+
if match
|
81
|
+
[match[1], match[2]]
|
82
|
+
else
|
83
|
+
@unknown_count += 1
|
84
|
+
[name, "unknown-#{@unknown_count}"]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def start_suite(suite_name)
|
89
|
+
@current_suite = TestSuite.new(suite_name)
|
90
|
+
@current_suite.start
|
91
|
+
end
|
92
|
+
|
93
|
+
def finish_suite
|
94
|
+
if @current_suite
|
95
|
+
@current_suite.finish
|
96
|
+
@report_manager.write_report(@current_suite)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def start_test(test_name)
|
101
|
+
tc = TestCase.new(test_name)
|
102
|
+
tc.start
|
103
|
+
@current_suite.testcases << tc
|
104
|
+
end
|
105
|
+
|
106
|
+
def finish_test(failure = nil)
|
107
|
+
tc = @current_suite.testcases.last
|
108
|
+
tc.finish
|
109
|
+
tc.failure = Failure.new(failure) if failure
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper.rb"
|
2
|
+
|
3
|
+
context "The ReportManager" do
|
4
|
+
setup do
|
5
|
+
@reports_dir = REPORTS_DIR
|
6
|
+
end
|
7
|
+
|
8
|
+
teardown do
|
9
|
+
FileUtils.rm_rf @reports_dir
|
10
|
+
ENV["CI_REPORTS"] = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "should create the report directory according to the given prefix" do
|
14
|
+
CI::Reporter::ReportManager.new("spec")
|
15
|
+
File.directory?(@reports_dir).should_be true
|
16
|
+
end
|
17
|
+
|
18
|
+
specify "should create the report directory based on CI_REPORTS environment variable if set" do
|
19
|
+
@reports_dir = "#{Dir.getwd}/dummy"
|
20
|
+
ENV["CI_REPORTS"] = @reports_dir
|
21
|
+
CI::Reporter::ReportManager.new("spec")
|
22
|
+
File.directory?(@reports_dir).should_be true
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "should write reports based on name and xml content of a test suite" do
|
26
|
+
reporter = CI::Reporter::ReportManager.new("spec")
|
27
|
+
suite = mock("test suite")
|
28
|
+
suite.should_receive(:name).and_return("some test suite name")
|
29
|
+
suite.should_receive(:to_xml).and_return("<xml></xml>")
|
30
|
+
reporter.write_report(suite)
|
31
|
+
filename = "#{REPORTS_DIR}/SPEC-some-test-suite-name.xml"
|
32
|
+
File.exist?(filename).should_be true
|
33
|
+
File.open(filename) {|f| f.read.should == "<xml></xml>"}
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper.rb"
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
context "The RSpec reporter" do
|
5
|
+
setup do
|
6
|
+
@error = mock("error")
|
7
|
+
@error.stub!(:exception).and_return do
|
8
|
+
begin
|
9
|
+
raise StandardError, "error message"
|
10
|
+
rescue => e
|
11
|
+
e
|
12
|
+
end
|
13
|
+
end
|
14
|
+
@error.stub!(:expectation_not_met?).and_return(false)
|
15
|
+
@report_mgr = mock("report manager")
|
16
|
+
@fmt = CI::Reporter::RSpec.new(StringIO.new(""), false, false, @report_mgr)
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "should create a test suite with one success and one failure" do
|
20
|
+
@report_mgr.should_receive(:write_report).and_return do |suite|
|
21
|
+
suite.testcases.length.should == 2
|
22
|
+
suite.testcases.first.should_not_be_failure
|
23
|
+
suite.testcases.first.should_not_be_error
|
24
|
+
suite.testcases.last.should_be_error
|
25
|
+
end
|
26
|
+
|
27
|
+
@fmt.start(2)
|
28
|
+
@fmt.add_context("A context", true)
|
29
|
+
@fmt.spec_started("should pass")
|
30
|
+
@fmt.spec_passed("should pass")
|
31
|
+
@fmt.spec_started("should fail")
|
32
|
+
@fmt.spec_failed("should fail", 1, @error)
|
33
|
+
@fmt.dump_summary(0.1, 2, 1)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper.rb"
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
context "A TestSuite" do
|
5
|
+
setup do
|
6
|
+
@suite = CI::Reporter::TestSuite.new("example suite")
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "should collect timings when start and finish are invoked in sequence" do
|
10
|
+
@suite.start
|
11
|
+
@suite.finish
|
12
|
+
@suite.time.should_be > 0
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "should aggregate tests" do
|
16
|
+
@suite.start
|
17
|
+
@suite.testcases << CI::Reporter::TestCase.new("example test")
|
18
|
+
@suite.finish
|
19
|
+
@suite.tests.should == 1
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "should indicate number of failures and errors" do
|
23
|
+
failure = mock("failure")
|
24
|
+
failure.stub!(:failure?).and_return true
|
25
|
+
failure.stub!(:error?).and_return false
|
26
|
+
|
27
|
+
error = mock("error")
|
28
|
+
error.stub!(:failure?).and_return false
|
29
|
+
error.stub!(:error?).and_return true
|
30
|
+
|
31
|
+
@suite.start
|
32
|
+
@suite.testcases << CI::Reporter::TestCase.new("example test")
|
33
|
+
@suite.testcases << CI::Reporter::TestCase.new("failure test")
|
34
|
+
@suite.testcases.last.failure = failure
|
35
|
+
@suite.testcases << CI::Reporter::TestCase.new("error test")
|
36
|
+
@suite.testcases.last.failure = error
|
37
|
+
@suite.finish
|
38
|
+
@suite.tests.should == 3
|
39
|
+
@suite.failures.should == 1
|
40
|
+
@suite.errors.should == 1
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
context "TestSuite xml" do
|
46
|
+
setup do
|
47
|
+
@suite = CI::Reporter::TestSuite.new("example suite")
|
48
|
+
begin
|
49
|
+
raise StandardError, "an exception occurred"
|
50
|
+
rescue => e
|
51
|
+
@exception = e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
specify "should contain Ant/JUnit-formatted description of entire suite" do
|
56
|
+
failure = mock("failure")
|
57
|
+
failure.stub!(:failure?).and_return true
|
58
|
+
failure.stub!(:error?).and_return false
|
59
|
+
failure.stub!(:name).and_return "failure"
|
60
|
+
failure.stub!(:message).and_return "There was a failure"
|
61
|
+
failure.stub!(:location).and_return @exception.backtrace.join("\n")
|
62
|
+
|
63
|
+
error = mock("error")
|
64
|
+
error.stub!(:failure?).and_return false
|
65
|
+
error.stub!(:error?).and_return true
|
66
|
+
error.stub!(:name).and_return "error"
|
67
|
+
error.stub!(:message).and_return "There was a error"
|
68
|
+
error.stub!(:location).and_return @exception.backtrace.join("\n")
|
69
|
+
|
70
|
+
@suite.start
|
71
|
+
@suite.testcases << CI::Reporter::TestCase.new("example test")
|
72
|
+
@suite.testcases << CI::Reporter::TestCase.new("failure test")
|
73
|
+
@suite.testcases.last.failure = failure
|
74
|
+
@suite.testcases << CI::Reporter::TestCase.new("error test")
|
75
|
+
@suite.testcases.last.failure = error
|
76
|
+
@suite.finish
|
77
|
+
|
78
|
+
xml = @suite.to_xml
|
79
|
+
doc = REXML::Document.new(xml)
|
80
|
+
testsuite = doc.root.elements.to_a('/testsuite')
|
81
|
+
testsuite.length.should == 1
|
82
|
+
testsuite = testsuite.first
|
83
|
+
|
84
|
+
testcases = testsuite.elements.to_a('testcase')
|
85
|
+
testcases.length.should == 3
|
86
|
+
end
|
87
|
+
|
88
|
+
specify "should filter attributes properly for invalid characters" do
|
89
|
+
failure = mock("failure")
|
90
|
+
failure.stub!(:failure?).and_return true
|
91
|
+
failure.stub!(:error?).and_return false
|
92
|
+
failure.stub!(:name).and_return "failure"
|
93
|
+
failure.stub!(:message).and_return "There was a <failure>\nReason: blah"
|
94
|
+
failure.stub!(:location).and_return @exception.backtrace.join("\n")
|
95
|
+
|
96
|
+
@suite.start
|
97
|
+
@suite.testcases << CI::Reporter::TestCase.new("failure test")
|
98
|
+
@suite.testcases.last.failure = failure
|
99
|
+
@suite.finish
|
100
|
+
|
101
|
+
xml = @suite.to_xml
|
102
|
+
xml.should_match %r/message="There was a <failure>\.\.\."/
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "A TestCase" do
|
107
|
+
setup do
|
108
|
+
@tc = CI::Reporter::TestCase.new("example test")
|
109
|
+
end
|
110
|
+
|
111
|
+
specify "should collect timings when start and finish are invoked in sequence" do
|
112
|
+
@tc.start
|
113
|
+
@tc.finish
|
114
|
+
@tc.time.should_be > 0
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper.rb"
|
2
|
+
|
3
|
+
context "The TestUnit reporter" do
|
4
|
+
setup do
|
5
|
+
@report_mgr = mock("report manager")
|
6
|
+
@testunit = CI::Reporter::TestUnit.new(nil, @report_mgr)
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "should build suites based on adjacent tests with the same class name" do
|
10
|
+
@suite = nil
|
11
|
+
@report_mgr.should_receive(:write_report).once.and_return {|suite| @suite = suite }
|
12
|
+
|
13
|
+
@testunit.started(mock("result"))
|
14
|
+
@testunit.test_started("test_one(TestCaseClass)")
|
15
|
+
@testunit.test_finished("test_one(TestCaseClass)")
|
16
|
+
@testunit.test_started("test_two(TestCaseClass)")
|
17
|
+
@testunit.test_finished("test_two(TestCaseClass)")
|
18
|
+
@testunit.finished(10)
|
19
|
+
|
20
|
+
@suite.name.should == "TestCaseClass"
|
21
|
+
@suite.testcases.length.should == 2
|
22
|
+
@suite.testcases.first.name.should == "test_one"
|
23
|
+
@suite.testcases.first.should_not_be_failure
|
24
|
+
@suite.testcases.first.should_not_be_error
|
25
|
+
@suite.testcases.last.name.should == "test_two"
|
26
|
+
@suite.testcases.last.should_not_be_failure
|
27
|
+
@suite.testcases.last.should_not_be_error
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "should build two suites when encountering different class names" do
|
31
|
+
@suites = []
|
32
|
+
@report_mgr.should_receive(:write_report).twice.and_return {|suite| @suites << suite }
|
33
|
+
|
34
|
+
@testunit.started(mock("result"))
|
35
|
+
@testunit.test_started("test_one(TestCaseClass)")
|
36
|
+
@testunit.test_finished("test_one(TestCaseClass)")
|
37
|
+
@testunit.test_started("test_two(AnotherTestCaseClass)")
|
38
|
+
@testunit.test_finished("test_two(AnotherTestCaseClass)")
|
39
|
+
@testunit.finished(10)
|
40
|
+
|
41
|
+
@suites.first.name.should == "TestCaseClass"
|
42
|
+
@suites.first.testcases.length.should == 1
|
43
|
+
@suites.first.testcases.first.name.should == "test_one"
|
44
|
+
@suites.last.name.should == "AnotherTestCaseClass"
|
45
|
+
@suites.last.testcases.length.should == 1
|
46
|
+
@suites.last.testcases.first.name.should == "test_two"
|
47
|
+
end
|
48
|
+
|
49
|
+
specify "should add failures to testcases when encountering a fault" do
|
50
|
+
begin
|
51
|
+
raise StandardError, "error"
|
52
|
+
rescue => e
|
53
|
+
@error = Test::Unit::Error.new("test_two(TestCaseClass)", e)
|
54
|
+
end
|
55
|
+
|
56
|
+
@suite = nil
|
57
|
+
@report_mgr.should_receive(:write_report).once.and_return {|suite| @suite = suite }
|
58
|
+
|
59
|
+
@testunit.started(mock("result"))
|
60
|
+
@testunit.test_started("test_one(TestCaseClass)")
|
61
|
+
@testunit.test_finished("test_one(TestCaseClass)")
|
62
|
+
@testunit.test_started("test_two(TestCaseClass)")
|
63
|
+
@testunit.fault(@error)
|
64
|
+
@testunit.finished(10)
|
65
|
+
|
66
|
+
@suite.name.should == "TestCaseClass"
|
67
|
+
@suite.testcases.length.should == 2
|
68
|
+
@suite.testcases.first.name.should == "test_one"
|
69
|
+
@suite.testcases.first.should_not_be_failure
|
70
|
+
@suite.testcases.first.should_not_be_error
|
71
|
+
@suite.testcases.last.name.should == "test_two"
|
72
|
+
@suite.testcases.last.should_not_be_failure
|
73
|
+
@suite.testcases.last.should_be_error
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
begin
|
2
|
+
gem 'ci_reporter'
|
3
|
+
rescue
|
4
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
5
|
+
end
|
6
|
+
require 'ci/reporter/rake/rspec'
|
7
|
+
require 'ci/reporter/rake/test_unit'
|
8
|
+
|
9
|
+
namespace :ci do
|
10
|
+
task :setup_rspec => "ci:setup:rspec"
|
11
|
+
task :setup_testunit => "ci:setup:testunit"
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: ci_reporter
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "1.0"
|
7
|
+
date: 2007-02-14 00:00:00 -06:00
|
8
|
+
summary: CI::Reporter allows you to generate reams of XML for use with continuous integration systems.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: nick@nicksieger.com
|
12
|
+
homepage: http://caldersphere.rubyforge.org/ci_reporter
|
13
|
+
rubyforge_project: caldersphere
|
14
|
+
description: CI::Reporter is an add-on to Test::Unit and RSpec that allows you to generate XML reports of your test and/or spec runs. The resulting files can be read by a continuous integration system that understands Ant's JUnit report XML format, thus allowing your CI system to track test/spec successes and failures.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Nick Sieger
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
- Rakefile
|
36
|
+
- lib/ci/reporter/core.rb
|
37
|
+
- lib/ci/reporter/report_manager.rb
|
38
|
+
- lib/ci/reporter/rspec.rb
|
39
|
+
- lib/ci/reporter/test_suite.rb
|
40
|
+
- lib/ci/reporter/test_unit.rb
|
41
|
+
- lib/ci/reporter/rake/rspec.rb
|
42
|
+
- lib/ci/reporter/rake/rspec_loader.rb
|
43
|
+
- lib/ci/reporter/rake/test_unit.rb
|
44
|
+
- lib/ci/reporter/rake/test_unit_loader.rb
|
45
|
+
- spec/spec_helper.rb
|
46
|
+
- spec/ci/reporter/report_manager_spec.rb
|
47
|
+
- spec/ci/reporter/rspec_spec.rb
|
48
|
+
- spec/ci/reporter/test_suite_spec.rb
|
49
|
+
- spec/ci/reporter/test_unit_spec.rb
|
50
|
+
- tasks/ci_reporter.rake
|
51
|
+
test_files:
|
52
|
+
- spec/ci/reporter/report_manager_spec.rb
|
53
|
+
- spec/ci/reporter/rspec_spec.rb
|
54
|
+
- spec/ci/reporter/test_suite_spec.rb
|
55
|
+
- spec/ci/reporter/test_unit_spec.rb
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
extra_rdoc_files: []
|
59
|
+
|
60
|
+
executables: []
|
61
|
+
|
62
|
+
extensions: []
|
63
|
+
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
dependencies: []
|
67
|
+
|