Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 3b7ee32466
Fetching contributors…

Cannot retrieve contributors at this time

file 157 lines (132 sloc) 4.845 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
require 'cucumber/formatter/ordered_xml_markup'
require 'cucumber/formatter/io'
require 'fileutils'

module Cucumber
  module Formatter
    # The formatter used for <tt>--format junit</tt>
    class Junit
      include Io
      
      class UnNamedFeatureError < StandardError
        def initialize(feature_file)
          super("The feature in '#{feature_file}' does not have a name. The JUnit XML format requires a name for the testsuite element.")
        end
      end
      
      def initialize(step_mother, io, options)
        @reportdir = ensure_dir(io, "junit")
        @options = options
      end

      def before_feature(feature)
        @current_feature = feature
        @failures = @errors = @tests = @skipped = 0
        @builder = OrderedXmlMarkup.new( :indent => 2 )
        @time = 0
      end
      
      def after_feature(feature)
        @testsuite = OrderedXmlMarkup.new( :indent => 2 )
        @testsuite.instruct!
        @testsuite.testsuite(
          :failures => @failures,
          :errors => @errors,
          :skipped => @skipped,
          :tests => @tests,
          :time => "%.6f" % @time,
          :name => @feature_name ) do
          @testsuite << @builder.target!
        end

        write_file(feature_result_filename(feature.file), @testsuite.target!)
      end

      def before_background(*args)
        @in_background = true
      end
      
      def after_background(*args)
        @in_background = false
      end

      def feature_name(keyword, name)
        raise UnNamedFeatureError.new(@current_feature.file) if name.empty?
        lines = name.split(/\r?\n/)
        @feature_name = lines[0]
      end

      def scenario_name(keyword, name, file_colon_line, source_indent)
        # TODO: What's all this ugly weird code doing? Why not just use keyword and name????
        scenario_name = name.strip.delete(".\r\n")
        scenario_name = "Unnamed scenario" if name == ""
        @scenario = scenario_name
        description = "Scenario"
        if keyword.include?('Scenario Outline')
          description << " outline"
          @in_examples = true
        end
        @output = "#{description}: #{@scenario}\n\n"
      end

      def before_steps(steps)
        @steps_start = Time.now
      end
      
      def after_steps(steps)
        return if @in_background || @in_examples
        
        duration = Time.now - @steps_start
        if steps.failed?
          steps.each { |step| @output += "#{step.keyword}#{step.name}\n" }
          @output += "\nMessage:\n"
        end
        build_testcase(duration, steps.status, steps.exception)
      end
      
      def before_examples(*args)
        @header_row = true
        @in_examples = true
      end
      
      def after_examples(*args)
        @in_examples = false
      end

      def before_table_row(table_row)
        return unless @in_examples

        @table_start = Time.now
      end

      def after_table_row(table_row)
        return unless @in_examples
        duration = Time.now - @table_start
        unless @header_row
          name_suffix = " (outline example : #{table_row.name})"
          if table_row.failed?
            @output += "Example row: #{table_row.name}\n"
            @output += "\nMessage:\n"
          end
          build_testcase(duration, table_row.status, table_row.exception, name_suffix)
        end
        
        @header_row = false if @header_row
      end

      private

      def build_testcase(duration, status, exception = nil, suffix = "")
        @time += duration
        classname = "#{@feature_name}.#{@scenario}"
        name = "#{@scenario}#{suffix}"
        pending = [:pending, :undefined].include?(status)
        passed = (status == :passed || (pending && !@options[:strict]))

        @builder.testcase(:classname => classname, :name => name, :time => "%.6f" % duration) do
          unless passed
            @builder.failure(:message => "#{status.to_s} #{name}", :type => status.to_s) do
              @builder.cdata! @output
              @builder.cdata!(format_exception(exception)) if exception
            end
            @failures += 1
          end
          if passed and (status == :skipped || pending)
            @builder.skipped
            @skipped += 1
          end
        end
        @tests += 1
      end

      def format_exception(exception)
        (["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n")
      end
      
      def feature_result_filename(feature_file)
        ext_length = File.extname(feature_file).length
        basename = File.basename(feature_file)[0...-ext_length]
        File.join(@reportdir, "TEST-#{basename}.xml")
      end
      
      def write_file(feature_filename, data)
        File.open(feature_filename, 'w') { |file| file.write(data) }
      end
    end
  end
end
Something went wrong with that request. Please try again.