mspec 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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