summaryrefslogtreecommitdiff
path: root/spec/mspec/lib/mspec/runner/formatters/base.rb
blob: c7c50c40d8b44e20f8fe6ae4d5e8f8751f0c64b8 (plain)
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
require 'mspec/expectations/expectations'
require 'mspec/runner/actions/timer'
require 'mspec/runner/actions/tally'
require 'mspec/utils/options'

if ENV['CHECK_LEAKS']
  require 'mspec/runner/actions/leakchecker'
  require 'mspec/runner/actions/constants_leak_checker'
end

class BaseFormatter
  attr_reader :exceptions, :timer, :tally

  def initialize(out = nil)
    @current_state = nil
    @exception = false
    @failure = false
    @exceptions = []

    @count = 0 # For subclasses

    if out
      @out = File.open out, "w"
    else
      @out = $stdout
    end

    err = MSpecOptions.latest && MSpecOptions.latest.config[:error_output]
    if err
      @err = (err == 'stderr') ? $stderr : File.open(err, "w")
    else
      @err = @out
    end
  end

  # Creates the +TimerAction+ and +TallyAction+ instances and registers them.
  def register
    (@timer = TimerAction.new).register
    (@tally = TallyAction.new).register
    @counter = @tally.counter

    if ENV['CHECK_LEAKS']
      save = ENV['CHECK_LEAKS'] == 'save'
      LeakCheckerAction.new.register
      ConstantsLeakCheckerAction.new(save).register
    end

    MSpec.register :abort,     self
    MSpec.register :before,    self
    MSpec.register :after,     self
    MSpec.register :exception, self
    MSpec.register :finish,    self
  end

  def abort
    if @current_state
      puts "\naborting example: #{@current_state.description}"
    end
  end

  # Returns true if any exception is raised while running
  # an example. This flag is reset before each example
  # is evaluated.
  def exception?
    @exception
  end

  # Returns true if all exceptions during the evaluation
  # of an example are failures rather than errors. See
  # <tt>ExceptionState#failure</tt>. This flag is reset
  # before each example is evaluated.
  def failure?
    @failure
  end

  # Callback for the MSpec :before event. Resets the
  # +#exception?+ and +#failure+ flags.
  def before(state = nil)
    @current_state = state
    @failure = @exception = false
  end

  # Callback for the MSpec :exception event. Stores the
  # +ExceptionState+ object to generate the list of backtraces
  # after all the specs are run. Also updates the internal
  # +#exception?+ and +#failure?+ flags.
  def exception(exception)
    @count += 1
    @failure = @exception ? @failure && exception.failure? : exception.failure?
    @exception = true
    @exceptions << exception
  end

  # Callback for the MSpec :after event.
  def after(state = nil)
    @current_state = nil
  end

  # Callback for the MSpec :start event. Calls :after event.
  # Defined here, in the base class, and used by MultiFormatter.
  def start
    after
  end

  # Callback for the MSpec :unload event. Calls :after event.
  # Defined here, in the base class, and used by MultiFormatter.
  def unload
    after
  end

  # Callback for the MSpec :finish event. Prints a description
  # and backtrace for every exception that occurred while
  # evaluating the examples.
  def finish
    print "\n"
    count = 0
    @exceptions.each do |exc|
      count += 1
      print_exception(exc, count)
    end
    print "\n#{@timer.format}\n\n#{@tally.format}\n"
  end

  def print_exception(exc, count)
    outcome = exc.failure? ? "FAILED" : "ERROR"
    @err.print "\n#{count})\n#{exc.description} #{outcome}\n"
    @err.print exc.message, "\n"
    @err.print exc.backtrace, "\n"
  end

  # A convenience method to allow printing to different outputs.
  def print(*args)
    @out.print(*args)
    @out.flush
  end
end