1require 'mspec/expectations/expectations' 2require 'mspec/runner/actions/timer' 3require 'mspec/runner/actions/tally' 4require 'mspec/runner/actions/leakchecker' if ENV['CHECK_LEAKS'] 5 6class DottedFormatter 7 attr_reader :exceptions, :timer, :tally 8 9 def initialize(out=nil) 10 @exception = @failure = false 11 @exceptions = [] 12 @count = 0 # For subclasses 13 if out.nil? 14 @out = $stdout 15 else 16 @out = File.open out, "w" 17 end 18 19 @current_state = nil 20 end 21 22 # Creates the +TimerAction+ and +TallyAction+ instances and 23 # registers them. Registers +self+ for the +:exception+, 24 # +:before+, +:after+, and +:finish+ actions. 25 def register 26 (@timer = TimerAction.new).register 27 (@tally = TallyAction.new).register 28 LeakCheckerAction.new.register if ENV['CHECK_LEAKS'] 29 @counter = @tally.counter 30 31 MSpec.register :exception, self 32 MSpec.register :before, self 33 MSpec.register :after, self 34 MSpec.register :finish, self 35 MSpec.register :abort, self 36 end 37 38 def abort 39 if @current_state 40 puts "\naborting example: #{@current_state.description}" 41 end 42 end 43 44 # Returns true if any exception is raised while running 45 # an example. This flag is reset before each example 46 # is evaluated. 47 def exception? 48 @exception 49 end 50 51 # Returns true if all exceptions during the evaluation 52 # of an example are failures rather than errors. See 53 # <tt>ExceptionState#failure</tt>. This flag is reset 54 # before each example is evaluated. 55 def failure? 56 @failure 57 end 58 59 # Callback for the MSpec :before event. Resets the 60 # +#exception?+ and +#failure+ flags. 61 def before(state=nil) 62 @current_state = state 63 @failure = @exception = false 64 end 65 66 # Callback for the MSpec :exception event. Stores the 67 # +ExceptionState+ object to generate the list of backtraces 68 # after all the specs are run. Also updates the internal 69 # +#exception?+ and +#failure?+ flags. 70 def exception(exception) 71 @count += 1 72 @failure = @exception ? @failure && exception.failure? : exception.failure? 73 @exception = true 74 @exceptions << exception 75 end 76 77 # Callback for the MSpec :after event. Prints an indicator 78 # for the result of evaluating this example as follows: 79 # . = No failure or error 80 # F = An SpecExpectationNotMetError was raised 81 # E = Any exception other than SpecExpectationNotMetError 82 def after(state = nil) 83 @current_state = nil 84 85 unless exception? 86 print "." 87 else 88 print failure? ? "F" : "E" 89 end 90 end 91 92 # Callback for the MSpec :finish event. Prints a description 93 # and backtrace for every exception that occurred while 94 # evaluating the examples. 95 def finish 96 print "\n" 97 count = 0 98 @exceptions.each do |exc| 99 count += 1 100 print_exception(exc, count) 101 end 102 print "\n#{@timer.format}\n\n#{@tally.format}\n" 103 end 104 105 def print_exception(exc, count) 106 outcome = exc.failure? ? "FAILED" : "ERROR" 107 print "\n#{count})\n#{exc.description} #{outcome}\n" 108 print exc.message, "\n" 109 print exc.backtrace, "\n" 110 end 111 112 # A convenience method to allow printing to different outputs. 113 def print(*args) 114 @out.print(*args) 115 @out.flush 116 end 117end 118