mspec 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ task :default => :spec
9
9
 
10
10
  spec = Gem::Specification.new do |s|
11
11
  s.name = %q{mspec}
12
- s.version = "1.1.1"
12
+ s.version = "1.2.0"
13
13
 
14
14
  s.specification_version = 2 if s.respond_to? :specification_version=
15
15
 
@@ -29,15 +29,35 @@ class TagAction < ActionFilter
29
29
  @tag = tag
30
30
  @comment = comment
31
31
  @report = []
32
+ @exception = false
32
33
  end
33
34
 
35
+ # Returns true if there are no _tag_ or _description_ filters. This
36
+ # means that a TagAction matches any example by default. Otherwise,
37
+ # returns true if either the _tag_ or the _description_ filter
38
+ # matches +string+.
34
39
  def ===(string)
35
40
  return true unless @sfilter or @tfilter
36
41
  @sfilter === string or @tfilter === string
37
42
  end
38
43
 
44
+ # Callback for the MSpec :before event. Resets the +#exception?+
45
+ # flag to false.
46
+ def before(state)
47
+ @exception = false
48
+ end
49
+
50
+ # Callback for the MSpec :exception event. Sets the +#exception?+
51
+ # flag.
52
+ def exception(exception)
53
+ @exception = exception
54
+ end
55
+
56
+ # Callback for the MSpec :after event. Performs the tag action
57
+ # depending on the type of action and the outcome of evaluating
58
+ # the example. See +TagAction+ for a description of the actions.
39
59
  def after(state)
40
- if self === state.description and outcome? state
60
+ if self === state.description and outcome?
41
61
  tag = SpecTag.new
42
62
  tag.tag = @tag
43
63
  tag.comment = @comment
@@ -54,10 +74,19 @@ class TagAction < ActionFilter
54
74
  end
55
75
  end
56
76
 
57
- def outcome?(state)
77
+ # Returns true if the result of evaluating the example matches
78
+ # the _outcome_ registered for this tag action. See +TagAction+
79
+ # for a description of the _outcome_ types.
80
+ def outcome?
58
81
  @outcome == :all or
59
- (@outcome == :pass and not state.exception?) or
60
- (@outcome == :fail and state.exception?)
82
+ (@outcome == :pass and not exception?) or
83
+ (@outcome == :fail and exception?)
84
+ end
85
+
86
+ # Returns true if an exception was raised while evaluating the
87
+ # current example.
88
+ def exception?
89
+ !!@exception
61
90
  end
62
91
 
63
92
  def report
@@ -65,6 +94,8 @@ class TagAction < ActionFilter
65
94
  end
66
95
  private :report
67
96
 
97
+ # Callback for the MSpec :finish event. Prints the actions
98
+ # performed while evaluating the examples.
68
99
  def finish
69
100
  case @action
70
101
  when :add
@@ -86,12 +117,15 @@ class TagAction < ActionFilter
86
117
 
87
118
  def register
88
119
  super
89
- MSpec.register :after, self
90
- MSpec.register :finish, self
120
+ MSpec.register :exception, self
121
+ MSpec.register :after, self
122
+ MSpec.register :finish, self
91
123
  end
92
124
 
93
125
  def unregister
94
126
  super
95
- MSpec.unregister :after, self
127
+ MSpec.unregister :exception, self
128
+ MSpec.unregister :after, self
129
+ MSpec.unregister :finish, self
96
130
  end
97
131
  end
@@ -48,14 +48,16 @@ class TallyAction
48
48
  end
49
49
 
50
50
  def register
51
- MSpec.register :load, self
52
- MSpec.register :after, self
51
+ MSpec.register :load, self
52
+ MSpec.register :exception, self
53
+ MSpec.register :after, self
53
54
  MSpec.register :expectation, self
54
55
  end
55
56
 
56
57
  def unregister
57
- MSpec.unregister :load, self
58
- MSpec.unregister :after, self
58
+ MSpec.unregister :load, self
59
+ MSpec.unregister :exception, self
60
+ MSpec.unregister :after, self
59
61
  MSpec.unregister :expectation, self
60
62
  end
61
63
 
@@ -63,15 +65,22 @@ class TallyAction
63
65
  @counter.files!
64
66
  end
65
67
 
68
+ # Callback for the MSpec :expectation event. Increments the
69
+ # tally of expectations (e.g. #should, #should_receive, etc.).
66
70
  def expectation(state)
67
71
  @counter.expectations!
68
72
  end
69
73
 
74
+ # Callback for the MSpec :exception event. Increments the
75
+ # tally of errors and failures.
76
+ def exception(exception)
77
+ exception.failure? ? @counter.failures! : @counter.errors!
78
+ end
79
+
80
+ # Callback for the MSpec :after event. Increments the tally
81
+ # of examples.
70
82
  def after(state)
71
83
  @counter.examples!
72
- state.exceptions.each do |msg, exc|
73
- state.failure?(exc) ? @counter.failures! : @counter.errors!
74
- end
75
84
  end
76
85
 
77
86
  def format
@@ -1,6 +1,18 @@
1
1
  require 'mspec/runner/mspec'
2
-
3
- class RunState
2
+ require 'mspec/runner/example'
3
+
4
+ # Holds the state of the +describe+ block that is being
5
+ # evaluated. Every example (i.e. +it+ block) is evaluated
6
+ # in a context, which may include state set up in <tt>before
7
+ # :each</tt> or <tt>before :all</tt> blocks.
8
+ #
9
+ #--
10
+ # A note on naming: this is named _ContextState_ rather
11
+ # than _DescribeState_ because +describe+ is the keyword
12
+ # in the DSL for refering to the context in which an example
13
+ # is evaluated, just as +it+ refers to the example itself.
14
+ #++
15
+ class ContextState
4
16
  def initialize
5
17
  @start = []
6
18
  @before = []
@@ -32,7 +44,7 @@ class RunState
32
44
  end
33
45
 
34
46
  def it(desc, &block)
35
- state = SpecState.new @describe, desc
47
+ state = ExampleState.new @describe, desc
36
48
  @spec << [desc, block, state] unless state.filtered?
37
49
  end
38
50
 
@@ -42,8 +54,8 @@ class RunState
42
54
  end
43
55
 
44
56
  def protect(what, blocks, check=true)
45
- return if check and MSpec.pretend_mode?
46
- Array(blocks).each { |block| MSpec.protect what, &block }
57
+ return false if check and MSpec.pretend_mode?
58
+ Array(blocks).all? { |block| MSpec.protect what, &block }
47
59
  end
48
60
 
49
61
  def process
@@ -56,10 +68,11 @@ class RunState
56
68
  @spec.each do |desc, spec, state|
57
69
  @state = state
58
70
  MSpec.actions :before, state
59
- protect "before :each", @before
60
- protect nil, spec
61
- protect "after :each", @after
62
- protect "Mock.verify_count", lambda { Mock.verify_count }
71
+ if protect("before :each", @before)
72
+ protect nil, spec
73
+ protect "after :each", @after
74
+ protect "Mock.verify_count", lambda { Mock.verify_count }
75
+ end
63
76
  protect "Mock.cleanup", lambda { Mock.cleanup }
64
77
  MSpec.actions :after, state
65
78
  @state = nil
@@ -69,48 +82,3 @@ class RunState
69
82
  end
70
83
  end
71
84
 
72
- class SpecState
73
- def initialize(describe, it)
74
- @describe = describe
75
- @it = it
76
- @unfiltered = nil
77
- end
78
-
79
- def describe
80
- @describe
81
- end
82
-
83
- def it
84
- @it
85
- end
86
-
87
- def description
88
- @description ||= "#{@describe} #{@it}"
89
- end
90
-
91
- def exceptions
92
- @exceptions ||= []
93
- end
94
-
95
- def exception?
96
- not exceptions.empty?
97
- end
98
-
99
- def unfiltered?
100
- unless @unfiltered
101
- incl = MSpec.retrieve(:include) || []
102
- excl = MSpec.retrieve(:exclude) || []
103
- @unfiltered = incl.empty? || incl.any? { |f| f === description }
104
- @unfiltered &&= excl.empty? || !excl.any? { |f| f === description }
105
- end
106
- @unfiltered
107
- end
108
-
109
- def filtered?
110
- not unfiltered?
111
- end
112
-
113
- def failure?(exception)
114
- exception.is_a?(ExpectationNotMetError)
115
- end
116
- end
@@ -0,0 +1,37 @@
1
+ require 'mspec/runner/mspec'
2
+
3
+ # Holds some of the state of the example (i.e. +it+ block) that is
4
+ # being evaluated. See also +ContextState+.
5
+ class ExampleState
6
+ def initialize(describe, it)
7
+ @describe = describe
8
+ @it = it
9
+ @unfiltered = nil
10
+ end
11
+
12
+ def describe
13
+ @describe
14
+ end
15
+
16
+ def it
17
+ @it
18
+ end
19
+
20
+ def description
21
+ @description ||= "#{@describe} #{@it}"
22
+ end
23
+
24
+ def unfiltered?
25
+ unless @unfiltered
26
+ incl = MSpec.retrieve(:include) || []
27
+ excl = MSpec.retrieve(:exclude) || []
28
+ @unfiltered = incl.empty? || incl.any? { |f| f === description }
29
+ @unfiltered &&= excl.empty? || !excl.any? { |f| f === description }
30
+ end
31
+ @unfiltered
32
+ end
33
+
34
+ def filtered?
35
+ not unfiltered?
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ class ExceptionState
2
+ attr_reader :description, :describe, :it, :exception
3
+
4
+ def initialize(state, location, exception)
5
+ @exception = exception
6
+
7
+ @description = location ? "An exception occurred during: #{location}" : ""
8
+ if state
9
+ @description << "\n" unless @description.empty?
10
+ @description << state.description
11
+ @describe = state.describe
12
+ @it = state.it
13
+ else
14
+ @describe = @it = ""
15
+ end
16
+ end
17
+
18
+ def failure?
19
+ @exception.is_a? ExpectationNotMetError
20
+ end
21
+
22
+ def message
23
+ if @exception.message.empty?
24
+ "<No message>"
25
+ elsif @exception.class == ExpectationNotMetError
26
+ @exception.message
27
+ else
28
+ "#{@exception.class}: #{@exception.message}"
29
+ end
30
+ end
31
+
32
+ def backtrace
33
+ begin
34
+ return @exception.awesome_backtrace.show
35
+ rescue Exception
36
+ return @exception.backtrace && @exception.backtrace.join("\n")
37
+ end
38
+ end
39
+ end
@@ -3,10 +3,12 @@ require 'mspec/runner/actions/timer'
3
3
  require 'mspec/runner/actions/tally'
4
4
 
5
5
  class DottedFormatter
6
- attr_reader :timer, :tally
6
+ attr_reader :exceptions, :timer, :tally
7
7
 
8
8
  def initialize(out=nil)
9
- @states = []
9
+ @exception = @failure = false
10
+ @exceptions = []
11
+ @count = 0
10
12
  if out.nil?
11
13
  @out = $stdout
12
14
  else
@@ -14,68 +16,82 @@ class DottedFormatter
14
16
  end
15
17
  end
16
18
 
19
+ # Creates the +TimerAction+ and +TallyAction+ instances and
20
+ # registers them. Registers +self+ for the +:exception+,
21
+ # +:before+, +:after+, and +:finish+ actions.
17
22
  def register
18
- @timer = TimerAction.new
19
- @timer.register
20
- @tally = TallyAction.new
21
- @tally.register
23
+ (@timer = TimerAction.new).register
24
+ (@tally = TallyAction.new).register
22
25
  @counter = @tally.counter
23
26
 
24
- MSpec.register :after, self
25
- MSpec.register :finish, self
27
+ MSpec.register :exception, self
28
+ MSpec.register :before, self
29
+ MSpec.register :after, self
30
+ MSpec.register :finish, self
26
31
  end
27
32
 
33
+ # Returns true if any exception is raised while running
34
+ # an example. This flag is reset before each example
35
+ # is evaluated.
36
+ def exception?
37
+ @exception
38
+ end
39
+
40
+ # Returns true if all exceptions during the evaluation
41
+ # of an example are failures rather than errors. See
42
+ # <tt>ExceptionState#failure</tt>. This flag is reset
43
+ # before each example is evaluated.
44
+ def failure?
45
+ @failure
46
+ end
47
+
48
+ # Callback for the MSpec :before event. Resets the
49
+ # +#exception?+ and +#failure+ flags.
50
+ def before(state)
51
+ @failure = @exception = false
52
+ end
53
+
54
+ # Callback for the MSpec :exception event. Stores the
55
+ # +ExceptionState+ object to generate the list of backtraces
56
+ # after all the specs are run. Also updates the internal
57
+ # +#exception?+ and +#failure?+ flags.
58
+ def exception(exception)
59
+ @count += 1
60
+ @failure = @exception ? @failure && exception.failure? : exception.failure?
61
+ @exception = true
62
+ @exceptions << exception
63
+ end
64
+
65
+ # Callback for the MSpec :after event. Prints an indicator
66
+ # for the result of evaluating this example as follows:
67
+ # . = No failure or error
68
+ # F = An ExpectationNotMetError was raised
69
+ # E = Any exception other than ExpectationNotMetError
28
70
  def after(state)
29
- unless state.exception?
71
+ unless exception?
30
72
  print "."
31
73
  else
32
- @states << state
33
- print failure?(state) ? "F" : "E"
74
+ print failure? ? "F" : "E"
34
75
  end
35
76
  end
36
77
 
78
+ # Callback for the MSpec :finish event. Prints a description
79
+ # and backtrace for every exception that occurred while
80
+ # evaluating the examples.
37
81
  def finish
38
82
  print "\n"
39
83
  count = 0
40
- @states.each do |state|
41
- state.exceptions.each do |msg, exc|
42
- outcome = failure?(state) ? "FAILED" : "ERROR"
43
- print "\n#{count += 1})\n#{state.description} #{outcome}\n"
44
- print "#{exc.class.name} occurred during: #{msg}\n" if msg
45
- print message(exc)
46
- print "\n"
47
- print backtrace(exc)
48
- print "\n"
49
- end
84
+ @exceptions.each do |exc|
85
+ outcome = exc.failure? ? "FAILED" : "ERROR"
86
+ print "\n#{count += 1})\n#{exc.description} #{outcome}\n"
87
+ print exc.message, "\n"
88
+ print exc.backtrace, "\n"
50
89
  end
51
90
  print "\n#{@timer.format}\n\n#{@tally.format}\n"
52
91
  end
53
92
 
93
+ # A convenience method to allow printing to different outputs.
54
94
  def print(*args)
55
95
  @out.print(*args)
56
96
  end
57
-
58
- def message(exc)
59
- if exc.message.empty?
60
- "<No message>"
61
- elsif exc.class == ExpectationNotMetError
62
- exc.message
63
- else
64
- "#{exc.class}: #{exc.message}"
65
- end
66
- end
67
-
68
- def failure?(state)
69
- state.exceptions.all? { |msg, exc| state.failure?(exc) }
70
- end
71
- private :failure?
72
-
73
- def backtrace(exc)
74
- begin
75
- return exc.awesome_backtrace.show
76
- rescue Exception
77
- return exc.backtrace && exc.backtrace.join("\n")
78
- end
79
- end
80
- private :backtrace
81
97
  end