rspec-core 2.8.0.rc1 → 2.8.0.rc2

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.
Files changed (73) hide show
  1. data/License.txt +24 -0
  2. data/README.md +197 -37
  3. data/features/command_line/format_option.feature +3 -3
  4. data/features/command_line/init.feature +18 -0
  5. data/features/example_groups/shared_examples.feature +1 -1
  6. data/features/hooks/around_hooks.feature +4 -4
  7. data/features/pending/pending_examples.feature +5 -5
  8. data/features/support/env.rb +8 -1
  9. data/lib/autotest/rspec2.rb +2 -6
  10. data/lib/rspec/core.rb +48 -158
  11. data/lib/rspec/core/backward_compatibility.rb +2 -0
  12. data/lib/rspec/core/command_line.rb +4 -0
  13. data/lib/rspec/core/configuration.rb +201 -106
  14. data/lib/rspec/core/configuration_options.rb +2 -1
  15. data/lib/rspec/core/deprecation.rb +2 -3
  16. data/lib/rspec/core/drb_options.rb +69 -58
  17. data/lib/rspec/core/dsl.rb +12 -0
  18. data/lib/rspec/core/example.rb +53 -18
  19. data/lib/rspec/core/example_group.rb +144 -54
  20. data/lib/rspec/core/extensions.rb +4 -4
  21. data/lib/rspec/core/extensions/instance_eval_with_args.rb +5 -0
  22. data/lib/rspec/core/extensions/kernel.rb +1 -1
  23. data/lib/rspec/core/extensions/module_eval_with_args.rb +4 -0
  24. data/lib/rspec/core/extensions/ordered.rb +7 -2
  25. data/lib/rspec/core/filter_manager.rb +69 -36
  26. data/lib/rspec/core/formatters/base_text_formatter.rb +1 -1
  27. data/lib/rspec/core/formatters/html_formatter.rb +10 -4
  28. data/lib/rspec/core/hooks.rb +93 -34
  29. data/lib/rspec/core/let.rb +62 -61
  30. data/lib/rspec/core/metadata.rb +103 -80
  31. data/lib/rspec/core/metadata_hash_builder.rb +4 -0
  32. data/lib/rspec/core/option_parser.rb +42 -44
  33. data/lib/rspec/core/pending.rb +25 -3
  34. data/lib/rspec/core/project_initializer.rb +62 -0
  35. data/lib/rspec/core/rake_task.rb +7 -13
  36. data/lib/rspec/core/runner.rb +2 -2
  37. data/lib/rspec/core/shared_context.rb +1 -1
  38. data/lib/rspec/core/shared_example_group.rb +11 -5
  39. data/lib/rspec/core/subject.rb +3 -3
  40. data/lib/rspec/core/version.rb +1 -1
  41. data/lib/rspec/monkey.rb +1 -1
  42. data/lib/rspec/monkey/spork/test_framework/rspec.rb +2 -0
  43. data/spec/command_line/order_spec.rb +19 -13
  44. data/spec/rspec/core/command_line_spec.rb +1 -5
  45. data/spec/rspec/core/configuration_options_spec.rb +22 -27
  46. data/spec/rspec/core/configuration_spec.rb +32 -14
  47. data/spec/rspec/core/drb_command_line_spec.rb +20 -37
  48. data/spec/rspec/core/drb_options_spec.rb +51 -3
  49. data/spec/rspec/core/example_group_spec.rb +101 -84
  50. data/spec/rspec/core/example_spec.rb +14 -0
  51. data/spec/rspec/core/filter_manager_spec.rb +114 -33
  52. data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +17 -69
  53. data/spec/rspec/core/formatters/html_formatted-1.8.7.html +14 -4
  54. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +14 -4
  55. data/spec/rspec/core/formatters/html_formatted-1.9.3.html +14 -4
  56. data/spec/rspec/core/formatters/html_formatter_spec.rb +1 -1
  57. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +20 -72
  58. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +24 -14
  59. data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +29 -19
  60. data/spec/rspec/core/formatters/text_mate_formatted-1.9.3.html +29 -19
  61. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +1 -2
  62. data/spec/rspec/core/metadata_spec.rb +77 -45
  63. data/spec/rspec/core/option_parser_spec.rb +5 -0
  64. data/spec/rspec/core/pending_example_spec.rb +44 -12
  65. data/spec/rspec/core/project_initializer_spec.rb +130 -0
  66. data/spec/rspec/core/rake_task_spec.rb +1 -1
  67. data/spec/spec_helper.rb +2 -0
  68. data/spec/support/matchers.rb +2 -12
  69. metadata +18 -16
  70. data/features/command_line/configure.feature +0 -22
  71. data/lib/rspec/core/command_line_configuration.rb +0 -62
  72. data/lib/rspec/core/errors.rb +0 -13
  73. data/spec/rspec/core/command_line_configuration_spec.rb +0 -26
@@ -2,23 +2,23 @@ module RSpec
2
2
  module Core
3
3
  module Let
4
4
 
5
- module ClassMethods
5
+ module ExampleGroupMethods
6
6
  # Generates a method whose return value is memoized
7
7
  # after the first call.
8
8
  #
9
9
  # @example
10
10
  #
11
- # describe Thing do
12
- # let(:thing) { Thing.new }
11
+ # describe Thing do
12
+ # let(:thing) { Thing.new }
13
13
  #
14
- # it "does something" do
15
- # # first invocation, executes block, memoizes and returns result
16
- # thing.do_something
14
+ # it "does something" do
15
+ # # first invocation, executes block, memoizes and returns result
16
+ # thing.do_something
17
17
  #
18
- # # second invocation, returns the memoized value
19
- # thing.should be_something
20
- # end
21
- # end
18
+ # # second invocation, returns the memoized value
19
+ # thing.should be_something
20
+ # end
21
+ # end
22
22
  def let(name, &block)
23
23
  define_method(name) do
24
24
  __memoized.fetch(name) {|k| __memoized[k] = instance_eval(&block) }
@@ -32,69 +32,70 @@ module RSpec
32
32
  #
33
33
  # @example
34
34
  #
35
- # class Thing
36
- # def self.count
37
- # @count ||= 0
38
- # end
39
- #
40
- # def self.count=(val)
41
- # @count += val
42
- # end
43
- #
44
- # def self.reset_count
45
- # @count = 0
46
- # end
47
- #
48
- # def initialize
49
- # self.class.count += 1
50
- # end
51
- # end
52
- #
53
- # describe Thing do
54
- # after(:each) { Thing.reset_count }
55
- #
56
- # context "using let" do
57
- # let(:thing) { Thing.new }
58
- #
59
- # it "is not invoked implicitly" do
60
- # Thing.count.should eq(0)
61
- # end
62
- #
63
- # it "can be invoked explicitly" do
64
- # thing
65
- # Thing.count.should eq(1)
66
- # end
67
- # end
68
- #
69
- # context "using let!" do
70
- # let!(:thing) { Thing.new }
71
- #
72
- # it "is invoked implicitly" do
73
- # Thing.count.should eq(1)
74
- # end
75
- #
76
- # it "returns memoized version on first invocation" do
77
- # thing
78
- # Thing.count.should eq(1)
79
- # end
80
- # end
81
- # end
35
+ # class Thing
36
+ # def self.count
37
+ # @count ||= 0
38
+ # end
39
+ #
40
+ # def self.count=(val)
41
+ # @count += val
42
+ # end
43
+ #
44
+ # def self.reset_count
45
+ # @count = 0
46
+ # end
47
+ #
48
+ # def initialize
49
+ # self.class.count += 1
50
+ # end
51
+ # end
52
+ #
53
+ # describe Thing do
54
+ # after(:each) { Thing.reset_count }
55
+ #
56
+ # context "using let" do
57
+ # let(:thing) { Thing.new }
58
+ #
59
+ # it "is not invoked implicitly" do
60
+ # Thing.count.should eq(0)
61
+ # end
62
+ #
63
+ # it "can be invoked explicitly" do
64
+ # thing
65
+ # Thing.count.should eq(1)
66
+ # end
67
+ # end
68
+ #
69
+ # context "using let!" do
70
+ # let!(:thing) { Thing.new }
71
+ #
72
+ # it "is invoked implicitly" do
73
+ # Thing.count.should eq(1)
74
+ # end
75
+ #
76
+ # it "returns memoized version on first invocation" do
77
+ # thing
78
+ # Thing.count.should eq(1)
79
+ # end
80
+ # end
81
+ # end
82
82
  def let!(name, &block)
83
83
  let(name, &block)
84
84
  before { __send__(name) }
85
85
  end
86
86
  end
87
87
 
88
- module InstanceMethods
89
- private
88
+ # @private
89
+ module ExampleMethods
90
+ # @private
90
91
  def __memoized
91
92
  @__memoized ||= {}
92
93
  end
93
94
  end
94
95
 
95
96
  def self.included(mod)
96
- mod.extend ClassMethods
97
- mod.__send__ :include, InstanceMethods
97
+ mod.extend ExampleGroupMethods
98
+ mod.__send__ :include, ExampleMethods
98
99
  end
99
100
 
100
101
  end
@@ -1,13 +1,35 @@
1
1
  module RSpec
2
2
  module Core
3
- # Each ExampleGroup class and Example instance ...
3
+ # Each ExampleGroup class and Example instance owns an instance of
4
+ # Metadata, which is Hash extended to support lazy evaluation of values
5
+ # associated with keys that may or may not be used by any example or group.
6
+ #
7
+ # In addition to metadata that is used internally, this also stores
8
+ # user-supplied metadata, e.g.
9
+ #
10
+ # describe Something, :type => :ui do
11
+ # it "does something", :slow => true do
12
+ # # ...
13
+ # end
14
+ # end
15
+ #
16
+ # `:type => :ui` is stored in the Metadata owned by the example group, and
17
+ # `:slow => true` is stored in the Metadata owned by the example. These can
18
+ # then be used to select which examples are run using the `--tag` option on
19
+ # the command line, or several methods on `Configuration` used to filter a
20
+ # run (e.g. `filter_run_including`, `filter_run_excluding`, etc).
4
21
  #
5
22
  # @see Example#metadata
6
23
  # @see ExampleGroup.metadata
24
+ # @see FilterManager
25
+ # @see Configuration#filter_run_including
26
+ # @see Configuration#filter_run_excluding
7
27
  class Metadata < Hash
8
28
 
29
+ # @private
9
30
  module MetadataHash
10
31
 
32
+ # @private
11
33
  # Supports lazy evaluation of some values. Extended by
12
34
  # ExampleMetadataHash and GroupMetadataHash, which get mixed in to
13
35
  # Metadata for ExampleGroups and Examples (respectively).
@@ -23,10 +45,13 @@ module RSpec
23
45
  super
24
46
  when :execution_result
25
47
  store(:execution_result, {})
26
- when :describes
27
- store(:describes, described_class_for(self))
48
+ when :describes, :described_class
49
+ klass = described_class
50
+ store(:described_class, klass)
51
+ # TODO (2011-11-07 DC) deprecate :describes as a key
52
+ store(:describes, klass)
28
53
  when :full_description
29
- store(:full_description, full_description_for(self))
54
+ store(:full_description, full_description)
30
55
  when :description
31
56
  store(:description, build_description_from(*self[:description_args]))
32
57
  else
@@ -34,7 +59,7 @@ module RSpec
34
59
  end
35
60
  end
36
61
 
37
- private
62
+ private
38
63
 
39
64
  def location
40
65
  "#{self[:file_path]}:#{self[:line_number]}"
@@ -49,16 +74,8 @@ module RSpec
49
74
  self[:caller].detect {|l| l !~ /\/lib\/rspec\/core/}
50
75
  end
51
76
 
52
- def described_class_for(m)
53
- m[:example_group][:describes]
54
- end
55
-
56
- def full_description_for(m)
57
- build_description_from(m[:example_group][:full_description], *m[:description_args])
58
- end
59
-
60
77
  def build_description_from(*parts)
61
- parts.map {|p| p.to_s}.reduce do |desc, p|
78
+ parts.map {|p| p.to_s}.inject do |desc, p|
62
79
  p =~ /^(#|::|\.)/ ? "#{desc}#{p}" : "#{desc} #{p}"
63
80
  end || ""
64
81
  end
@@ -68,6 +85,14 @@ module RSpec
68
85
  # lazy evaluation of some values.
69
86
  module ExampleMetadataHash
70
87
  include MetadataHash
88
+
89
+ def described_class
90
+ self[:example_group].described_class
91
+ end
92
+
93
+ def full_description
94
+ build_description_from(self[:example_group][:full_description], *self[:description_args])
95
+ end
71
96
  end
72
97
 
73
98
  # Mixed in to Metadata for an ExampleGroup (extends MetadataHash) to
@@ -75,14 +100,13 @@ module RSpec
75
100
  module GroupMetadataHash
76
101
  include MetadataHash
77
102
 
78
- private
79
-
80
- def described_class_for(*)
81
- ancestors.each do |g|
82
- return g[:describes] if g.has_key?(:describes)
103
+ def described_class
104
+ container_stack.each do |g|
105
+ return g[:describes] if g.has_key?(:describes)
106
+ return g[:described_class] if g.has_key?(:described_class)
83
107
  end
84
108
 
85
- ancestors.reverse.each do |g|
109
+ container_stack.reverse.each do |g|
86
110
  candidate = g[:description_args].first
87
111
  return candidate unless String === candidate || Symbol === candidate
88
112
  end
@@ -90,30 +114,26 @@ module RSpec
90
114
  nil
91
115
  end
92
116
 
93
- def full_description_for(*)
94
- build_description_from(*ancestors.reverse.map do |a|
95
- a[:description_args]
96
- end.flatten)
117
+ def full_description
118
+ build_description_from(*container_stack.reverse.map {|a| a[:description_args]}.flatten)
97
119
  end
98
120
 
99
- private
100
-
101
- def ancestors
102
- @ancestors ||= begin
103
- groups = [group = self]
104
- while group.has_key?(:example_group)
105
- groups << group[:example_group]
106
- group = group[:example_group]
107
- end
108
- groups
109
- end
121
+ def container_stack
122
+ @container_stack ||= begin
123
+ groups = [group = self]
124
+ while group.has_key?(:example_group)
125
+ groups << group[:example_group]
126
+ group = group[:example_group]
127
+ end
128
+ groups
129
+ end
110
130
  end
111
131
  end
112
132
 
113
133
  def initialize(parent_group_metadata=nil)
114
134
  if parent_group_metadata
115
135
  update(parent_group_metadata)
116
- store(:example_group, {:example_group => parent_group_metadata[:example_group]}.extend(GroupMetadataHash))
136
+ store(:example_group, {:example_group => parent_group_metadata[:example_group].extend(GroupMetadataHash)}.extend(GroupMetadataHash))
117
137
  else
118
138
  store(:example_group, {}.extend(GroupMetadataHash))
119
139
  end
@@ -121,7 +141,7 @@ module RSpec
121
141
  yield self if block_given?
122
142
  end
123
143
 
124
- # @api private
144
+ # @private
125
145
  def process(*args)
126
146
  user_metadata = args.last.is_a?(Hash) ? args.pop : {}
127
147
  ensure_valid_keys(user_metadata)
@@ -132,36 +152,29 @@ module RSpec
132
152
  update(user_metadata)
133
153
  end
134
154
 
135
- # @api private
155
+ # @private
136
156
  def for_example(description, user_metadata)
137
157
  dup.extend(ExampleMetadataHash).configure_for_example(description, user_metadata)
138
158
  end
139
159
 
140
- # @api private
160
+ # @private
141
161
  def any_apply?(filters)
142
162
  filters.any? {|k,v| filter_applies?(k,v)}
143
163
  end
144
164
 
145
- # @api private
165
+ # @private
146
166
  def all_apply?(filters)
147
167
  filters.all? {|k,v| filter_applies?(k,v)}
148
168
  end
149
169
 
150
- # @api private
170
+ # @private
151
171
  def filter_applies?(key, value, metadata=self)
172
+ return metadata.filter_applies_to_any_value?(key, value) if Array === metadata[key]
173
+ return metadata.line_number_filter_applies?(value) if key == :line_numbers
174
+ return metadata.location_filter_applies?(value) if key == :locations
175
+ return metadata.filters_apply?(key, value) if Hash === value
176
+
152
177
  case value
153
- when Hash
154
- if key == :locations
155
- file_path = (self[:example_group] || {})[:file_path]
156
- expanded_path = file_path && File.expand_path( file_path )
157
- if expanded_path && line_numbers = value[expanded_path]
158
- filter_applies?(:line_numbers, line_numbers)
159
- else
160
- true
161
- end
162
- else
163
- value.all? { |k, v| filter_applies?(k, v, metadata[key]) }
164
- end
165
178
  when Regexp
166
179
  metadata[key] =~ value
167
180
  when Proc
@@ -176,21 +189,35 @@ module RSpec
176
189
  else
177
190
  value.call(metadata[key]) rescue false
178
191
  end
179
- when String
180
- metadata[key].to_s == value.to_s
181
- when Enumerable
182
- if key == :line_numbers
183
- preceding_declaration_lines = value.map{|v| world.preceding_declaration_line(v)}
184
- !(relevant_line_numbers(metadata) & preceding_declaration_lines).empty?
185
- else
186
- metadata[key] == value
187
- end
188
192
  else
189
193
  metadata[key].to_s == value.to_s
190
194
  end
191
195
  end
192
196
 
193
- protected
197
+ # @private
198
+ def filters_apply?(key, value)
199
+ value.all? {|k, v| filter_applies?(k, v, self[key])}
200
+ end
201
+
202
+ # @private
203
+ def filter_applies_to_any_value?(key, value)
204
+ self[key].any? {|v| filter_applies?(key, v, {key => value})}
205
+ end
206
+
207
+ # @private
208
+ def location_filter_applies?(locations)
209
+ # it ignores location filters for other files
210
+ line_number = example_group_declaration_line(locations)
211
+ line_number ? line_number_filter_applies?(line_number) : true
212
+ end
213
+
214
+ # @private
215
+ def line_number_filter_applies?(line_numbers)
216
+ preceding_declaration_lines = line_numbers.map {|n| RSpec.world.preceding_declaration_line(n)}
217
+ !(relevant_line_numbers & preceding_declaration_lines).empty?
218
+ end
219
+
220
+ protected
194
221
 
195
222
  def configure_for_example(description, user_metadata)
196
223
  store(:description_args, [description])
@@ -198,7 +225,7 @@ module RSpec
198
225
  update(user_metadata)
199
226
  end
200
227
 
201
- private
228
+ private
202
229
 
203
230
  RESERVED_KEYS = [
204
231
  :description,
@@ -212,37 +239,33 @@ module RSpec
212
239
 
213
240
  def ensure_valid_keys(user_metadata)
214
241
  RESERVED_KEYS.each do |key|
215
- if user_metadata.keys.include?(key)
242
+ if user_metadata.has_key?(key)
216
243
  raise <<-EOM
217
- #{"*"*50}
244
+ #{"*"*50}
218
245
  :#{key} is not allowed
219
246
 
220
247
  RSpec reserves some hash keys for its own internal use,
221
248
  including :#{key}, which is used on:
222
249
 
223
- #{caller(0)[4]}.
250
+ #{caller(0)[4]}.
224
251
 
225
252
  Here are all of RSpec's reserved hash keys:
226
253
 
227
- #{RESERVED_KEYS.join("\n ")}
228
- #{"*"*50}
229
- EOM
230
- raise ":#{key} is not allowed"
254
+ #{RESERVED_KEYS.join("\n ")}
255
+ #{"*"*50}
256
+ EOM
231
257
  end
232
258
  end
233
259
  end
234
260
 
235
- def world
236
- RSpec.world
261
+ def example_group_declaration_line(locations)
262
+ locations[File.expand_path(self[:example_group][:file_path])] if self[:example_group]
237
263
  end
238
264
 
239
- def relevant_line_numbers(metadata)
240
- line_numbers = [metadata[:line_number]]
241
- if metadata[:example_group]
242
- line_numbers + relevant_line_numbers(metadata[:example_group])
243
- else
244
- line_numbers
245
- end
265
+ # TODO - make this a method on metadata - the problem is
266
+ # metadata[:example_group] is not always a kind of GroupMetadataHash.
267
+ def relevant_line_numbers(metadata=self)
268
+ [metadata[:line_number]] + (metadata[:example_group] ? relevant_line_numbers(metadata[:example_group]) : [])
246
269
  end
247
270
 
248
271
  end