rspec-junit-formatter-0.4.1/ 0000755 0001750 0001750 00000000000 13714003130 014774 5 ustar pravi pravi rspec-junit-formatter-0.4.1/lib/ 0000755 0001750 0001750 00000000000 13714003130 015542 5 ustar pravi pravi rspec-junit-formatter-0.4.1/lib/rspec_junit_formatter/ 0000755 0001750 0001750 00000000000 13714003130 022152 5 ustar pravi pravi rspec-junit-formatter-0.4.1/lib/rspec_junit_formatter/rspec3.rb 0000644 0001750 0001750 00000007726 13714003130 023712 0 ustar pravi pravi class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
RSpec::Core::Formatters.register self,
:start,
:stop,
:dump_summary
def start(notification)
@start_notification = notification
@started = Time.now
super
end
def stop(notification)
@examples_notification = notification
end
def dump_summary(notification)
@summary_notification = notification
without_color { xml_dump }
end
private
attr_reader :started
def example_count
@summary_notification.example_count
end
def pending_count
@summary_notification.pending_count
end
def failure_count
@summary_notification.failure_count
end
def duration
@summary_notification.duration
end
def examples
@examples_notification.notifications
end
def result_of(notification)
notification.example.execution_result.status
end
def example_group_file_path_for(notification)
metadata = notification.example.metadata[:example_group]
while parent_metadata = metadata[:parent_example_group]
metadata = parent_metadata
end
metadata[:file_path]
end
def classname_for(notification)
fp = example_group_file_path_for(notification)
fp.sub(%r{\.[^/]*\Z}, "").gsub("/", ".").gsub(%r{\A\.+|\.+\Z}, "")
end
def duration_for(notification)
notification.example.execution_result.run_time
end
def description_for(notification)
notification.example.full_description
end
def failure_type_for(example)
exception_for(example).class.name
end
def failure_message_for(example)
strip_diff_colors(exception_for(example).to_s)
end
def failure_for(notification)
strip_diff_colors(notification.message_lines.join("\n")) << "\n" << notification.formatted_backtrace.join("\n")
end
def exception_for(notification)
notification.example.execution_result.exception
end
# rspec makes it really difficult to swap in configuration temporarily due to
# the way it cascades defaults, command line arguments, and user
# configuration. This method makes sure configuration gets swapped in
# correctly, but also that the original state is definitely restored.
def swap_rspec_configuration(key, value)
unset = Object.new
force = RSpec.configuration.send(:value_for, key) { unset }
if unset.equal?(force)
previous = RSpec.configuration.send(key)
RSpec.configuration.send(:"#{key}=", value)
else
RSpec.configuration.force({key => value})
end
yield
ensure
if unset.equal?(force)
RSpec.configuration.send(:"#{key}=", previous)
else
RSpec.configuration.force({key => force})
end
end
# Completely gross hack for absolutely forcing off colorising for the
# duration of a block.
if RSpec.configuration.respond_to?(:color_mode=)
def without_color(&block)
swap_rspec_configuration(:color_mode, :off, &block)
end
elsif RSpec.configuration.respond_to?(:color=)
def without_color(&block)
swap_rspec_configuration(:color, false, &block)
end
else
warn 'rspec_junit_formatter cannot prevent colorising due to an unexpected RSpec.configuration format'
def without_color
yield
end
end
def stdout_for(example_notification)
example_notification.example.metadata[:stdout]
end
def stderr_for(example_notification)
example_notification.example.metadata[:stderr]
end
end
# rspec-core 3.0.x forgot to mark this as a module function which causes:
#
# NoMethodError: undefined method `wrap' for RSpec::Core::Notifications::NullColorizer:Class
# .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:229:in `add_shared_group_line'
# .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:157:in `message_lines'
#
if defined?(RSpec::Core::Notifications::NullColorizer) && RSpec::Core::Notifications::NullColorizer.is_a?(Class) && !RSpec::Core::Notifications::NullColorizer.respond_to?(:wrap)
RSpec::Core::Notifications::NullColorizer.class_eval do
def self.wrap(*args)
new.wrap(*args)
end
end
end
rspec-junit-formatter-0.4.1/lib/rspec_junit_formatter/rspec2.rb 0000644 0001750 0001750 00000003526 13714003130 023703 0 ustar pravi pravi class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
attr_reader :started
def start(example_count)
@started = Time.now
super
end
def dump_summary(duration, example_count, failure_count, pending_count)
super
xml_dump
end
private
def result_of(example)
example.execution_result[:status].to_sym
end
def example_group_file_path_for(example)
meta = example.metadata
while meta[:example_group]
meta = meta[:example_group]
end
meta[:file_path]
end
def classname_for(example)
fp = example_group_file_path_for(example)
fp.sub(%r{\.[^/.]+\Z}, "").gsub("/", ".").gsub(/\A\.+|\.+\Z/, "")
end
def duration_for(example)
example.execution_result[:run_time]
end
def description_for(example)
example.full_description
end
def exception_for(example)
example.execution_result[:exception]
end
def failure_type_for(example)
exception_for(example).class.name
end
def failure_message_for(example)
strip_diff_colors(exception_for(example).to_s)
end
def failure_for(example)
exception = exception_for(example)
message = strip_diff_colors(exception.message)
backtrace = format_backtrace(exception.backtrace, example)
if shared_group = find_shared_group(example)
backtrace << "Shared Example Group: \"#{shared_group.metadata[:shared_group_name]}\" called from #{shared_group.metadata[:example_group][:location]}"
end
"#{message}\n#{backtrace.join("\n")}"
end
def find_shared_group(example)
group_and_parent_groups(example).find { |group| group.metadata[:shared_group_name] }
end
def group_and_parent_groups(example)
example.example_group.parent_groups + [example.example_group]
end
def stdout_for(example)
example.metadata[:stdout]
end
def stderr_for(example)
example.metadata[:stderr]
end
end
rspec-junit-formatter-0.4.1/lib/rspec_junit_formatter.rb 0000644 0001750 0001750 00000013411 13714003130 022477 0 ustar pravi pravi require "socket"
require "time"
require "rspec/core"
require "rspec/core/formatters/base_formatter"
# Dumps rspec results as a JUnit XML file.
# Based on XML schema: http://windyroad.org/dl/Open%20Source/JUnit.xsd
class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
# rspec 2 and 3 implements are in separate files.
private
def xml_dump
output << %{\n}
output << %{\n}
output << %{\n}
output << %{\n}
output << %{\n}
xml_dump_examples
output << %{\n}
end
def xml_dump_examples
examples.each do |example|
case result_of(example)
when :pending
xml_dump_pending(example)
when :failed
xml_dump_failed(example)
else
xml_dump_example(example)
end
end
end
def xml_dump_pending(example)
xml_dump_example(example) do
output << %{}
end
end
def xml_dump_failed(example)
xml_dump_example(example) do
output << %{}
output << escape(failure_for(example))
output << %{}
end
end
def xml_dump_example(example)
output << %{}
yield if block_given?
xml_dump_output(example)
output << %{\n}
end
def xml_dump_output(example)
if (stdout = stdout_for(example)) && !stdout.empty?
output << %{}
output << escape(stdout)
output << %{}
end
if (stderr = stderr_for(example)) && !stderr.empty?
output << %{}
output << escape(stderr)
output << %{}
end
end
# Inversion of character range from https://www.w3.org/TR/xml/#charsets
ILLEGAL_REGEXP = Regexp.new(
"[^" <<
"\u{9}" << # => \t
"\u{a}" << # => \n
"\u{d}" << # => \r
"\u{20}-\u{d7ff}" <<
"\u{e000}-\u{fffd}" <<
"\u{10000}-\u{10ffff}" <<
"]"
)
# Replace illegals with a Ruby-like escape
ILLEGAL_REPLACEMENT = Hash.new { |_, c|
x = c.ord
if x <= 0xff
"\\x%02X".freeze % x
elsif x <= 0xffff
"\\u%04X".freeze % x
else
"\\u{%X}".freeze % x
end.freeze
}.update(
"\0".freeze => "\\0".freeze,
"\a".freeze => "\\a".freeze,
"\b".freeze => "\\b".freeze,
"\f".freeze => "\\f".freeze,
"\v".freeze => "\\v".freeze,
"\e".freeze => "\\e".freeze,
).freeze
# Discouraged characters from https://www.w3.org/TR/xml/#charsets
# Plus special characters with well-known entity replacements
DISCOURAGED_REGEXP = Regexp.new(
"[" <<
"\u{22}" << # => "
"\u{26}" << # => &
"\u{27}" << # => '
"\u{3c}" << # => <
"\u{3e}" << # => >
"\u{7f}-\u{84}" <<
"\u{86}-\u{9f}" <<
"\u{fdd0}-\u{fdef}" <<
"\u{1fffe}-\u{1ffff}" <<
"\u{2fffe}-\u{2ffff}" <<
"\u{3fffe}-\u{3ffff}" <<
"\u{4fffe}-\u{4ffff}" <<
"\u{5fffe}-\u{5ffff}" <<
"\u{6fffe}-\u{6ffff}" <<
"\u{7fffe}-\u{7ffff}" <<
"\u{8fffe}-\u{8ffff}" <<
"\u{9fffe}-\u{9ffff}" <<
"\u{afffe}-\u{affff}" <<
"\u{bfffe}-\u{bffff}" <<
"\u{cfffe}-\u{cffff}" <<
"\u{dfffe}-\u{dffff}" <<
"\u{efffe}-\u{effff}" <<
"\u{ffffe}-\u{fffff}" <<
"\u{10fffe}-\u{10ffff}" <<
"]"
)
# Translate well-known entities, or use generic unicode hex entity
DISCOURAGED_REPLACEMENTS = Hash.new { |_, c| "#{c.ord.to_s(16)};".freeze }.update(
?".freeze => """.freeze,
?&.freeze => "&".freeze,
?'.freeze => "'".freeze,
?<.freeze => "<".freeze,
?>.freeze => ">".freeze,
).freeze
def escape(text)
# Make sure it's utf-8, replace illegal characters with ruby-like escapes, and replace special and discouraged characters with entities
text.to_s.encode(Encoding::UTF_8).gsub(ILLEGAL_REGEXP, ILLEGAL_REPLACEMENT).gsub(DISCOURAGED_REGEXP, DISCOURAGED_REPLACEMENTS)
end
STRIP_DIFF_COLORS_BLOCK_REGEXP = /^ ( [ ]* ) Diff: (?: \e\[ 0 m )? (?: \n \1 \e\[ \d+ (?: ; \d+ )* m .* )* /x
STRIP_DIFF_COLORS_CODES_REGEXP = /\e\[ \d+ (?: ; \d+ )* m/x
def strip_diff_colors(string)
# XXX: RSpec diffs are appended to the message lines fairly early and will
# contain ANSI escape codes for colorizing terminal output if the global
# rspec configuration is turned on, regardless of which notification lines
# we ask for. We need to strip the codes from the diff part of the message
# for XML output here.
#
# We also only want to target the diff hunks because the failure message
# itself might legitimately contain ansi escape codes.
#
string.sub(STRIP_DIFF_COLORS_BLOCK_REGEXP) { |match| match.gsub(STRIP_DIFF_COLORS_CODES_REGEXP, "".freeze) }
end
end
RspecJunitFormatter = RSpecJUnitFormatter
if Gem::Version.new(RSpec::Core::Version::STRING) >= Gem::Version.new("3")
require "rspec_junit_formatter/rspec3"
else
require "rspec_junit_formatter/rspec2"
end
rspec-junit-formatter-0.4.1/checksums.yaml.gz.sig 0000444 0001750 0001750 00000000400 13714003130 021035 0 ustar pravi pravi (3E2M;8K`8"}Z /3ftKPa :n&Sc9Z;:ozЛ|W4+"ޟ]EIi\U3S41%nRn>t{z94=QDXNM+C$IJ/?=y.`Et*I< 9kG_Jq=z#v)=R6ۺZ\OKeh.y
ņ[`*S4g\: rspec-junit-formatter-0.4.1/data.tar.gz.sig 0000444 0001750 0001750 00000000400 13714003130 017605 0 ustar pravi pravi R%3i]^./J3hzJ#pF5
61D(f4o )j=P0(J;e'k̰cԒRn3 iP2+nk]zwM*{BBqu94e]ăf6ۣJ2_^|-AI,[jAqoŬk 0KC'@Xb*jCRVo340r"c"KӅTF6Y>k01|B rspec-junit-formatter-0.4.1/README.md 0000644 0001750 0001750 00000007573 13714003130 016267 0 ustar pravi pravi # RSpec JUnit Formatter
[](https://travis-ci.org/sj26/rspec_junit_formatter)
[](https://rubygems.org/gems/rspec_junit_formatter)
[RSpec][rspec] 2 & 3 results that your CI can read. [Jenkins][jenkins-junit], [Buildkite][buildkite-junit], [CircleCI][circleci-junit], and probably more, too.
[rspec]: http://rspec.info/
[jenkins-junit]: https://jenkins.io/doc/pipeline/steps/junit/
[buildkite-junit]: https://github.com/buildkite/rspec-junit-example
[circleci-junit]: https://circleci.com/docs/2.0/collect-test-data/
## Usage
Install the gem:
```sh
gem install rspec_junit_formatter
```
Use it:
```sh
rspec --format RspecJunitFormatter --out rspec.xml
```
You'll get an XML file `rspec.xml` with your results in it.
You can use it in combination with other [formatters][rspec-formatters], too:
```sh
rspec --format progress --format RspecJunitFormatter --out rspec.xml
```
[rspec-formatters]: https://relishapp.com/rspec/rspec-core/v/3-6/docs/formatters
### Using in your project with Bundler
Add it to your Gemfile if you're using [Bundler][bundler]. Put it in the same groups as rspec.
```ruby
group :test do
gem "rspec"
gem "rspec_junit_formatter"
end
```
Put the same arguments as the commands above in [your `.rspec`][rspec-file]:
```sh
--format RspecJunitFormatter
--out rspec.xml
```
[bundler]: https://bundler.io
[rspec-file]: https://relishapp.com/rspec/rspec-core/v/3-6/docs/configuration/read-command-line-configuration-options-from-files
### Parallel tests
For use with `parallel_tests`, add `$TEST_ENV_NUMBER` in the output file option (in `.rspec` or `.rspec_parallel`) to avoid concurrent process write conflicts.
```sh
--format RspecJunitFormatter
--out tmp/rspec<%= ENV["TEST_ENV_NUMBER"] %>.xml
```
The formatter includes `$TEST_ENV_NUMBER` in the test suite name within the XML, too.
### Capturing output
If you like, you can capture the standard output and error streams of each test into the `:stdout` and `:stderr` example metadata which will be added to the junit report, e.g.:
```ruby
# spec_helper.rb
RSpec.configure do |config|
# register around filter that captures stdout and stderr
config.around(:each) do |example|
$stdout = StringIO.new
$stderr = StringIO.new
example.run
example.metadata[:stdout] = $stdout.string
example.metadata[:stderr] = $stderr.string
$stdout = STDOUT
$stderr = STDERR
end
end
```
## Caveats
* XML can only represent a [limited subset of characters][xml-charsets] which excludes null bytes and most control characters. This gem will use character entities where possible and fall back to replacing invalid characters with Ruby-like escape codes otherwise. For example, the null byte becomes `\0`.
[xml-charsets]: https://www.w3.org/TR/xml/#charsets
## Roadmap
* It would be nice to split things up into individual test suites, although would this correspond to example groups? The subject? The spec file? Not sure yet.
## Development
Run the specs with `bundle exec rake`, which uses [Appraisal][appraisal] to run the specs against all supported versions of rspec.
[appraisal]: https://github.com/thoughtbot/appraisal
## Releasing
Bump the gem version in the gemspec, and commit. Then `bundle exec rake build` to build a gem package, `bundle exec rake install` to install and test it locally, then `bundle exec rake release` to tag and push the commits and gem.
## License
The MIT License, see [LICENSE](./LICENSE).
## Thanks
Inspired by the work of [Diego Souza][dgvncsz0f] on [RSpec Formatters][dgvncsz0f/rspec_formatters] after frustration with [CI Reporter][ci_reporter].
[dgvncsz0f]: https://github.com/dgvncsz0f
[dgvncsz0f/rspec_formatters]: https://github.com/dgvncsz0f/rspec_formatters
[ci_reporter]: https://github.com/nicksieger/ci_reporter
rspec-junit-formatter-0.4.1/metadata.gz.sig 0000444 0001750 0001750 00000000400 13714003130 017667 0 ustar pravi pravi l@$ lYo
^+0 2&^w^bV1aɺq[
T aC(y540`o1+
-+([ā#0ԜGنqHKQؼ'rNYAfwOC'^w
F%FV'5q:~V/ϥ|,d;e)}W{)px5e95o\Е̝Ro&` ő rspec-junit-formatter-0.4.1/rspec_junit_formatter.gemspec 0000644 0001750 0001750 00000006450 13714003130 022756 0 ustar pravi pravi #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
# stub: rspec_junit_formatter 0.4.1 ruby lib
Gem::Specification.new do |s|
s.name = "rspec_junit_formatter".freeze
s.version = "0.4.1"
s.required_rubygems_version = Gem::Requirement.new(">= 2.0.0".freeze) if s.respond_to? :required_rubygems_version=
s.metadata = { "changelog_uri" => "https://github.com/sj26/rspec_junit_formatter/blob/master/CHANGELOG.md" } if s.respond_to? :metadata=
s.require_paths = ["lib".freeze]
s.authors = ["Samuel Cochran".freeze]
s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDKDCCAhCgAwIBAgIBBTANBgkqhkiG9w0BAQUFADA6MQ0wCwYDVQQDDARzajI2\nMRQwEgYKCZImiZPyLGQBGRYEc2oyNjETMBEGCgmSJomT8ixkARkWA2NvbTAeFw0x\nNzA3MzEwNTQ3MDVaFw0xODA3MzEwNTQ3MDVaMDoxDTALBgNVBAMMBHNqMjYxFDAS\nBgoJkiaJk/IsZAEZFgRzajI2MRMwEQYKCZImiZPyLGQBGRYDY29tMIIBIjANBgkq\nhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr60Eo/ttCk8GMTMFiPr3GoYMIMFvLak\nxSmTk9YGCB6UiEePB4THSSA5w6IPyeaCF/nWkDp3/BAam0eZMWG1IzYQB23TqIM0\n1xzcNRvFsn0aQoQ00k+sj+G83j3T5OOV5OZIlu8xAChMkQmiPd1NXc6uFv+Iacz7\nkj+CMsI9YUFdNoU09QY0b+u+Rb6wDYdpyvN60YC30h0h1MeYbvYZJx/iZK4XY5zu\n4O/FL2ChjL2CPCpLZW55ShYyrzphWJwLOJe+FJ/ZBl6YXwrzQM9HKnt4titSNvyU\nKzE3L63A3PZvExzLrN9u09kuWLLJfXB2sGOlw3n9t72rJiuBr3/OQQIDAQABozkw\nNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU99dfRjEKFyczTeIz\nm3ZsDWrNC80wDQYJKoZIhvcNAQEFBQADggEBADGiXpvK754s0zTFx3y31ZRDdvAI\nlA209JIjUlDyr9ptCRadihyfF2l9/hb+hLemiPEYppzG6vEK1TIyzbAR36yOJ8CX\n4vPkCXLuwHhs6UIRbwN+IEy41nsIlBxmjLYei8h3t/G2Vm2oOaLdbjDXS+Srl9U8\nshsE8ft81PxSQfzEL7Mr9cC9XvWbHW+SyTpfGm8rAtaqZkNeke4U8a0di4oz2EfA\nP4lSfmXxsd1C71ckIp0cyXkPhyTtpyS/5hq9HhuUNzEHkSDe36/Rd1xYKV5JxMC2\nYAttWFUs06lor2q1wwncPaMtUtbWwW35+1IV6xhs2rFY6DD/I6ZkK3GnHdY=\n-----END CERTIFICATE-----\n".freeze]
s.date = "2018-05-30"
s.description = "RSpec results that your continuous integration service can read.".freeze
s.email = "sj26@sj26.com".freeze
s.files = ["LICENSE".freeze, "README.md".freeze, "lib/rspec_junit_formatter.rb".freeze, "lib/rspec_junit_formatter/rspec2.rb".freeze, "lib/rspec_junit_formatter/rspec3.rb".freeze]
s.homepage = "http://github.com/sj26/rspec_junit_formatter".freeze
s.licenses = ["MIT".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 2.0.0".freeze)
s.rubygems_version = "3.1.2".freeze
s.summary = "RSpec JUnit XML formatter".freeze
if s.respond_to? :specification_version then
s.specification_version = 4
end
if s.respond_to? :add_runtime_dependency then
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, ["~> 1.8", ">= 1.8.2"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_runtime_dependency(%q.freeze, [">= 2", "< 4", "!= 2.12.0"])
else
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, ["~> 1.8", ">= 1.8.2"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 2", "< 4", "!= 2.12.0"])
end
end
rspec-junit-formatter-0.4.1/LICENSE 0000644 0001750 0001750 00000002047 13714003130 016004 0 ustar pravi pravi Copyright (c) 2011-2018 Samuel Cochran
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.