class RSpecJUnitFormatter
Based on XML schema: windyroad.org/dl/Open%20Source/JUnit.xsd<br>Dumps rspec results as a JUnit XML file.
def classname_for(example)
def classname_for(example) fp = example_group_file_path_for(example) fp.sub(%r{\.[^/.]+\Z}, "").gsub("/", ".").gsub(/\A\.+|\.+\Z/, "") end
def classname_for(notification)
def classname_for(notification) fp = example_group_file_path_for(notification) fp.sub(%r{\.[^/]*\Z}, "").gsub("/", ".").gsub(%r{\A\.+|\.+\Z}, "") end
def description_for(example)
def description_for(example) example.full_description end
def description_for(notification)
def description_for(notification) notification.example.full_description end
def dump_summary(duration, example_count, failure_count, pending_count)
def dump_summary(duration, example_count, failure_count, pending_count) super xml_dump end
def dump_summary(notification)
def dump_summary(notification) @summary_notification = notification xml_dump end
def duration
def duration @summary_notification.duration end
def duration_for(example)
def duration_for(example) example.execution_result[:run_time] end
def duration_for(notification)
def duration_for(notification) notification.example.execution_result.run_time end
def escape(text)
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
def example_count
def example_count @summary_notification.example_count end
def example_group_file_path_for(example)
def example_group_file_path_for(example) meta = example.metadata while meta[:example_group] meta = meta[:example_group] end meta[:file_path] end
def example_group_file_path_for(notification)
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 examples
def examples @examples_notification.notifications end
def exception_for(example)
def exception_for(example) example.execution_result[:exception] end
def exception_for(notification)
def exception_for(notification) notification.example.execution_result.exception end
def failure_count
def failure_count @summary_notification.failure_count end
def failure_for(example)
def failure_for(example) exception = exception_for(example) 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 "#{exception.message}\n#{backtrace.join("\n")}" end
def failure_for(notification)
def failure_for(notification) strip_diff_colors(notification.message_lines.join("\n")) << "\n" << notification.formatted_backtrace.join("\n") end
def failure_message_for(example)
def failure_message_for(example) exception_for(example).to_s end
def failure_message_for(example)
def failure_message_for(example) strip_diff_colors(exception_for(example).to_s) end
def failure_type_for(example)
def failure_type_for(example) exception_for(example).class.name end
def failure_type_for(example)
def failure_type_for(example) exception_for(example).class.name end
def find_shared_group(example)
def find_shared_group(example) group_and_parent_groups(example).find { |group| group.metadata[:shared_group_name] } end
def group_and_parent_groups(example)
def group_and_parent_groups(example) example.example_group.parent_groups + [example.example_group] end
def pending_count
def pending_count @summary_notification.pending_count end
def result_of(example)
def result_of(example) example.execution_result[:status].to_sym end
def result_of(notification)
def result_of(notification) notification.example.execution_result.status end
def start(example_count)
def start(example_count) @started = Time.now super end
def start(notification)
def start(notification) @start_notification = notification @started = Time.now super end
def stop(notification)
def stop(notification) @examples_notification = notification end
def strip_diff_colors(string)
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
def xml_dump
def xml_dump output << %{<?xml version="1.0" encoding="UTF-8"?>\n} output << %{<testsuite} output << %{ name="rspec#{escape(ENV["TEST_ENV_NUMBER"].to_s)}"} output << %{ tests="#{example_count}"} output << %{ skipped="#{pending_count}"} output << %{ failures="#{failure_count}"} output << %{ errors="0"} output << %{ time="#{escape("%.6f" % duration)}"} output << %{ timestamp="#{escape(started.iso8601)}"} output << %{ hostname="#{escape(Socket.gethostname)}"} output << %{>\n} output << %{<properties>\n} output << %{<property} output << %{ name="seed"} output << %{ value="#{escape(RSpec.configuration.seed.to_s)}"} output << %{/>\n} output << %{</properties>\n} xml_dump_examples output << %{</testsuite>\n} end
def xml_dump_example(example)
def xml_dump_example(example) output << %{<testcase} output << %{ classname="#{escape(classname_for(example))}"} output << %{ name="#{escape(description_for(example))}"} output << %{ file="#{escape(example_group_file_path_for(example))}"} output << %{ time="#{escape("%.6f" % duration_for(example))}"} output << %{>} yield if block_given? output << %{</testcase>\n} end
def xml_dump_examples
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_failed(example)
def xml_dump_failed(example) exception = exception_for(example) xml_dump_example(example) do output << %{<failure} output << %{ message="#{escape(failure_message_for(example))}"} output << %{ type="#{escape(failure_type_for(example))}"} output << %{>} output << escape(failure_for(example)) output << %{</failure>} end end
def xml_dump_pending(example)
def xml_dump_pending(example) xml_dump_example(example) do output << %{<skipped/>} end end