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