lib/spec/runner/formatter/base_text_formatter.rb



require 'spec/runner/formatter/base_formatter'

module Spec
  module Runner
    module Formatter
      # Baseclass for text-based formatters. Can in fact be used for
      # non-text based ones too - just ignore the +output+ constructor
      # argument.
      class BaseTextFormatter < BaseFormatter
        attr_reader :output, :pending_examples
        # Creates a new instance that will write to +where+. If +where+ is a
        # String, output will be written to the File with that name, otherwise
        # +where+ is exected to be an IO (or an object that responds to #puts and #write).
        def initialize(options, where)
          super
          if where.is_a?(String)
            @output = File.open(where, 'w')
          elsif where == STDOUT
            @output = Kernel
            def @output.flush
              STDOUT.flush
            end
          else
            @output = where
          end
          @pending_examples = []
        end
        
        def example_pending(example, message)
          @pending_examples << [example.__full_description, message]
        end
        
        def dump_failure(counter, failure)
          @output.puts
          @output.puts "#{counter.to_s})"
          @output.puts colourise("#{failure.header}\n#{failure.exception.message}", failure)
          @output.puts format_backtrace(failure.exception.backtrace)
          @output.flush
        end
        
        def colourise(s, failure)
          if(failure.expectation_not_met?)
            red(s)
          elsif(failure.pending_fixed?)
            blue(s)
          else
            magenta(s)
          end
        end
      
        def dump_summary(duration, example_count, failure_count, pending_count)
          return if dry_run?
          @output.puts
          @output.puts "Finished in #{duration} seconds"
          @output.puts

          summary = "#{example_count} example#{'s' unless example_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}"
          summary << ", #{pending_count} pending" if pending_count > 0  

          if failure_count == 0
            if pending_count > 0
              @output.puts yellow(summary)
            else
              @output.puts green(summary)
            end
          else
            @output.puts red(summary)
          end
          @output.flush
        end

        def dump_pending
          unless @pending_examples.empty?
            @output.puts
            @output.puts "Pending:"
            @pending_examples.each do |pending_example|
              @output.puts "#{pending_example[0]} (#{pending_example[1]})" 
            end
          end
          @output.flush
        end
        
        def close
          if IO === @output
            @output.close 
          end
        end
        
        def format_backtrace(backtrace)
          return "" if backtrace.nil?
          backtrace.map { |line| backtrace_line(line) }.join("\n")
        end
      
      protected

        def colour?
          @options.colour ? true : false
        end

        def dry_run?
          @options.dry_run ? true : false
        end
        
        def backtrace_line(line)
          line.sub(/\A([^:]+:\d+)$/, '\\1:')
        end

        def colour(text, colour_code)
          return text unless colour? && output_to_tty?
          "#{colour_code}#{text}\e[0m"
        end

        def output_to_tty?
          begin
            @output == Kernel || @output.tty?
          rescue NoMethodError
            false
          end
        end
        
        def green(text); colour(text, "\e[32m"); end
        def red(text); colour(text, "\e[31m"); end
        def magenta(text); colour(text, "\e[35m"); end
        def yellow(text); colour(text, "\e[33m"); end
        def blue(text); colour(text, "\e[34m"); end
        
      end
    end
  end
end