rspec-core 2.7.1 → 2.8.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/README.md +1 -1
  2. data/features/command_line/order.feature +29 -0
  3. data/features/command_line/tag.feature +10 -9
  4. data/features/configuration/default_path.feature +2 -2
  5. data/features/filtering/exclusion_filters.feature +1 -1
  6. data/features/filtering/run_all_when_everything_filtered.feature +1 -1
  7. data/features/subject/attribute_of_subject.feature +1 -1
  8. data/lib/rspec/core.rb +148 -12
  9. data/lib/rspec/core/command_line.rb +2 -2
  10. data/lib/rspec/core/configuration.rb +300 -155
  11. data/lib/rspec/core/configuration_options.rb +34 -53
  12. data/lib/rspec/core/deprecation.rb +4 -0
  13. data/lib/rspec/core/drb_options.rb +72 -0
  14. data/lib/rspec/core/example.rb +58 -24
  15. data/lib/rspec/core/example_group.rb +10 -5
  16. data/lib/rspec/core/extensions.rb +1 -0
  17. data/lib/rspec/core/extensions/ordered.rb +16 -0
  18. data/lib/rspec/core/filter_manager.rb +170 -0
  19. data/lib/rspec/core/formatters/base_formatter.rb +3 -1
  20. data/lib/rspec/core/formatters/base_text_formatter.rb +6 -0
  21. data/lib/rspec/core/formatters/snippet_extractor.rb +1 -1
  22. data/lib/rspec/core/hooks.rb +197 -1
  23. data/lib/rspec/core/let.rb +3 -2
  24. data/lib/rspec/core/metadata.rb +25 -4
  25. data/lib/rspec/core/option_parser.rb +89 -54
  26. data/lib/rspec/core/pending.rb +41 -0
  27. data/lib/rspec/core/rake_task.rb +9 -25
  28. data/lib/rspec/core/reporter.rb +43 -19
  29. data/lib/rspec/core/shared_context.rb +35 -0
  30. data/lib/rspec/core/shared_example_group.rb +0 -1
  31. data/lib/rspec/core/subject.rb +4 -4
  32. data/lib/rspec/core/version.rb +1 -1
  33. data/lib/rspec/core/world.rb +34 -52
  34. data/spec/autotest/failed_results_re_spec.rb +2 -2
  35. data/spec/command_line/order_spec.rb +131 -0
  36. data/spec/rspec/core/command_line_spec.rb +2 -1
  37. data/spec/rspec/core/configuration_options_spec.rb +83 -163
  38. data/spec/rspec/core/configuration_spec.rb +311 -139
  39. data/spec/rspec/core/drb_options_spec.rb +131 -0
  40. data/spec/rspec/core/example_group_spec.rb +22 -11
  41. data/spec/rspec/core/example_spec.rb +1 -2
  42. data/spec/rspec/core/filter_manager_spec.rb +175 -0
  43. data/spec/rspec/core/formatters/helpers_spec.rb +1 -1
  44. data/spec/rspec/core/formatters/html_formatter_spec.rb +3 -2
  45. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +1 -1
  46. data/spec/rspec/core/metadata_spec.rb +21 -6
  47. data/spec/rspec/core/option_parser_spec.rb +74 -0
  48. data/spec/rspec/core/reporter_spec.rb +18 -1
  49. data/spec/rspec/core/shared_context_spec.rb +54 -17
  50. data/spec/rspec/core/subject_spec.rb +1 -1
  51. data/spec/rspec/core/world_spec.rb +7 -188
  52. data/spec/spec_helper.rb +47 -43
  53. data/spec/support/config_options_helper.rb +27 -0
  54. metadata +28 -12
  55. data/lib/rspec/core/expecting/with_rspec.rb +0 -9
  56. data/lib/rspec/core/expecting/with_stdlib.rb +0 -9
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # rspec-core
2
2
 
3
- RSpec Core provides the structure for writing executable examples of how your
3
+ rspec-core provides the structure for writing executable examples of how your
4
4
  code should behave.
5
5
 
6
6
  ## Install
@@ -0,0 +1,29 @@
1
+ Feature: --order (new in rspec-core-2.8)
2
+
3
+ Use the `--order` option to tell RSpec how to order the files, groups, and
4
+ examples. Options are `default` and `rand`:
5
+
6
+ Default is:
7
+
8
+ * files are ordered based on the underlying file system's order (typically
9
+ case-sensitive alpha on *nix OS's and case-insenstive alpha in Windows)
10
+ * groups/examples are loaded in the order in which they are declared
11
+
12
+ Use `rand` to randomize the order of files, groups within files, and
13
+ examples within groups.*
14
+
15
+ * Nested groups are always run from top-level to bottom-level in order to avoid
16
+ executing `before(:all)` and `after(:all)` hooks more than once, but the order
17
+ of groups at each level is randomized.
18
+
19
+ You can also specify a seed
20
+
21
+ <h3>Examples</h3>
22
+
23
+ --order default
24
+ --order rand
25
+ --order rand:123
26
+ --seed 123 # same as --order rand:123
27
+
28
+ The `default` option is only necessary when you have `--order rand` stored in a
29
+ config file (e.g. `.rspec`) and you want to override it from the command line.
@@ -33,19 +33,19 @@ Feature: --tag option
33
33
 
34
34
  Scenario: filter examples with a simple tag
35
35
  When I run `rspec . --tag focus`
36
- Then the output should contain "Run filtered including {:focus=>true}"
36
+ Then the output should contain "include {:focus=>true}"
37
37
  And the examples should all pass
38
38
 
39
39
  Scenario: filter examples with a simple tag and @
40
40
  When I run `rspec . --tag @focus`
41
- Then the output should contain "Run filtered including {:focus=>true}"
41
+ Then the output should contain "include {:focus=>true}"
42
42
  Then the examples should all pass
43
43
 
44
44
  Scenario: filter examples with a name:value tag
45
45
  When I run `rspec . --tag type:special`
46
46
  Then the output should contain:
47
47
  """
48
- Run filtered including {:type=>"special"}
48
+ include {:type=>"special"}
49
49
  """
50
50
  And the output should contain "2 examples"
51
51
  And the examples should all pass
@@ -54,25 +54,25 @@ Feature: --tag option
54
54
  When I run `rspec . --tag @type:special`
55
55
  Then the output should contain:
56
56
  """
57
- Run filtered including {:type=>"special"}
57
+ include {:type=>"special"}
58
58
  """
59
59
  And the examples should all pass
60
60
 
61
61
  Scenario: exclude examples with a simple tag
62
62
  When I run `rspec . --tag ~skip`
63
- Then the output should contain "Run filtered excluding {:skip=>true}"
63
+ Then the output should contain "exclude {:skip=>true}"
64
64
  Then the examples should all pass
65
65
 
66
66
  Scenario: exclude examples with a simple tag and @
67
67
  When I run `rspec . --tag ~@skip`
68
- Then the output should contain "Run filtered excluding {:skip=>true}"
68
+ Then the output should contain "exclude {:skip=>true}"
69
69
  Then the examples should all pass
70
70
 
71
71
  Scenario: exclude examples with a name:value tag
72
72
  When I run `rspec . --tag ~speed:slow`
73
73
  Then the output should contain:
74
74
  """
75
- Run filtered excluding {:speed=>"slow"}
75
+ exclude {:speed=>"slow"}
76
76
  """
77
77
  Then the examples should all pass
78
78
 
@@ -80,11 +80,12 @@ Feature: --tag option
80
80
  When I run `rspec . --tag ~@speed:slow`
81
81
  Then the output should contain:
82
82
  """
83
- Run filtered excluding {:speed=>"slow"}
83
+ exclude {:speed=>"slow"}
84
84
  """
85
85
  Then the examples should all pass
86
86
 
87
87
  Scenario: filter examples with a simple tag, exclude examples with another tag
88
88
  When I run `rspec . --tag focus --tag ~skip`
89
- Then the output should contain "Run filtered including {:focus=>true}, excluding {:skip=>true}"
89
+ Then the output should contain "include {:focus=>true}"
90
+ And the output should contain "exclude {:skip=>true}"
90
91
  And the examples should all pass
@@ -1,7 +1,7 @@
1
1
  Feature: default_path
2
2
 
3
- As of rspec-2.7 (not yet released), you can just type `rspec` to run all
4
- specs that live in the `spec` directory.
3
+ As of rspec-2.7, you can just type `rspec` to run all specs that live
4
+ in the `spec` directory.
5
5
 
6
6
  This is supported by a `--default_path` option, which is set to `spec` by
7
7
  default. If you prefer to keep your specs in a different directory, or assign
@@ -81,7 +81,7 @@ Feature: exclusion filters
81
81
  end
82
82
  """
83
83
  When I run `rspec ./spec/sample_spec.rb --format doc`
84
- Then the output should match /No examples were matched. Perhaps \{.*:broken=>true.*\} is excluding everything?/
84
+ Then the output should match /All examples were filtered out/
85
85
  And the examples should all pass
86
86
  And the output should not contain "group 1"
87
87
  And the output should not contain "group 2"
@@ -32,7 +32,7 @@ Feature: run all when everything filtered
32
32
  end
33
33
  """
34
34
  When I run `rspec spec/sample_spec.rb --format doc`
35
- Then the output should contain "No examples matched {:focus=>true}"
35
+ Then the output should contain "All examples were filtered out; ignoring {:focus=>true}"
36
36
  And the examples should all pass
37
37
  And the output should contain:
38
38
  """
@@ -65,7 +65,7 @@ Feature: attribute of subject
65
65
  Person
66
66
  with one phone number (555-1212)
67
67
  phone_numbers.first
68
- should eq 555-1212
68
+ should eq "555-1212"
69
69
  """
70
70
 
71
71
  Scenario: specify value of an attribute of a hash
@@ -1,4 +1,4 @@
1
- $rspec_start_time ||= Time.now
1
+ require 'rspec/core/filter_manager'
2
2
  require 'rspec/core/dsl'
3
3
  require 'rspec/core/extensions'
4
4
  require 'rspec/core/load_path'
@@ -17,37 +17,40 @@ require 'rspec/core/world'
17
17
  require 'rspec/core/configuration'
18
18
  require 'rspec/core/command_line_configuration'
19
19
  require 'rspec/core/option_parser'
20
+ require 'rspec/core/drb_options'
20
21
  require 'rspec/core/configuration_options'
21
22
  require 'rspec/core/command_line'
22
23
  require 'rspec/core/drb_command_line'
23
24
  require 'rspec/core/runner'
24
25
  require 'rspec/core/example'
25
- require 'rspec/core/shared_context'
26
26
  require 'rspec/core/shared_example_group'
27
27
  require 'rspec/core/example_group'
28
28
  require 'rspec/core/version'
29
29
  require 'rspec/core/errors'
30
30
 
31
31
  module RSpec
32
- autoload :Matchers, 'rspec/matchers'
33
-
34
- SharedContext = Core::SharedContext
32
+ autoload :Matchers, 'rspec/matchers'
33
+ autoload :SharedContext, 'rspec/core/shared_context'
35
34
 
35
+ # @api private
36
36
  # Used internally to determine what to do when a SIGINT is received
37
37
  def self.wants_to_quit
38
38
  world.wants_to_quit
39
39
  end
40
40
 
41
+ # @api private
41
42
  # Used internally to determine what to do when a SIGINT is received
42
43
  def self.wants_to_quit=(maybe)
43
44
  world.wants_to_quit=(maybe)
44
45
  end
45
46
 
47
+ # @api private
46
48
  # Internal container for global non-configuration data
47
49
  def self.world
48
50
  @world ||= RSpec::Core::World.new
49
51
  end
50
52
 
53
+ # @api private
51
54
  # Used internally to ensure examples get reloaded between multiple runs in
52
55
  # the same process.
53
56
  def self.reset
@@ -55,26 +58,159 @@ module RSpec
55
58
  configuration.reset
56
59
  end
57
60
 
58
- # Returns the global configuration object
61
+ # Returns the global [Configuration](Core/Configuration) object. While you
62
+ # _can_ use this method to access the configuration, the more common
63
+ # convention is to use [RSpec.configure](RSpec#configure-class_method).
64
+ #
65
+ # @example
66
+ # RSpec.configuration.drb_port = 1234
67
+ # @see RSpec.configure
68
+ # @see Core::Configuration
59
69
  def self.configuration
60
70
  @configuration ||= RSpec::Core::Configuration.new
61
71
  end
62
72
 
63
- # Yields the global configuration object
64
- #
65
- # == Examples
73
+ # @yield [Configuration] global configuration
66
74
  #
67
- # RSpec.configure do |config|
68
- # config.format = 'documentation'
69
- # end
75
+ # @example
76
+ # RSpec.configure do |config|
77
+ # config.add_formatter 'documentation'
78
+ # end
79
+ # @see Core::Configuration
70
80
  def self.configure
71
81
  yield configuration if block_given?
72
82
  end
73
83
 
84
+ # @api private
74
85
  # Used internally to clear remaining groups when fail_fast is set
75
86
  def self.clear_remaining_example_groups
76
87
  world.example_groups.clear
77
88
  end
89
+
90
+ # rspec-core provides the structure for writing executable examples of how
91
+ # your code should behave. It uses the words "describe" and "it" so we can
92
+ # express concepts like a conversation:
93
+ #
94
+ # "Describe an order."
95
+ # "It sums the prices of its line items."
96
+ #
97
+ # ## Basic structure
98
+ #
99
+ # describe Order do
100
+ # it "sums the prices of its line items" do
101
+ # order = Order.new
102
+ # order.add_entry(LineItem.new(:item => Item.new(
103
+ # :price => Money.new(1.11, :USD)
104
+ # )
105
+ # order.add_entry(LineItem.new(:item => Item.new(
106
+ # :price => Money.new(2.22, :USD),
107
+ # :quantity => 2
108
+ # )
109
+ # order.total.should eq(Money.new(5.55, :USD))
110
+ # end
111
+ # end
112
+ #
113
+ # The `describe` method creates an [ExampleGroup](Core/ExampleGroup). Within the
114
+ # block passed to `describe` you can declare examples using the `it` method.
115
+ #
116
+ # Under the hood, an example group is a class in which the block passed to
117
+ # `describe` is evaluated. The blocks passed to `it` are evaluated in the
118
+ # context of an _instance_ of that class.
119
+ #
120
+ # ## Nested groups
121
+ #
122
+ # You can also declare nested nested groups using the `describe` or `context`
123
+ # methods:
124
+ #
125
+ # describe Order to
126
+ # context "with no items" do
127
+ # it "behaves one way" do
128
+ # # ...
129
+ # end
130
+ # end
131
+ #
132
+ # context "with one item" do
133
+ # it "behaves another way" do
134
+ # # ...
135
+ # end
136
+ # end
137
+ # end
138
+ #
139
+ # ## Aliases
140
+ #
141
+ # You can declare example groups using either `describe` or `context`, though
142
+ # only `describe` is available at the top level.
143
+ #
144
+ # You can declare examples within a group using any of `it`, `specify`, or
145
+ # `example`.
146
+ #
147
+ # ## Shared examples
148
+ #
149
+ # Declare a shared example group using `shared_examples`, and then include it
150
+ # in each group using `include_examples`.
151
+ #
152
+ # shared_examples "collections" do |collection_class|
153
+ # it "is empty when first created" do
154
+ # collection_class.new.should be_empty
155
+ # end
156
+ # end
157
+ #
158
+ # describe Array do
159
+ # include_examples "collections", Array
160
+ # end
161
+ #
162
+ # ## Metadata
163
+ #
164
+ # rspec-core stores a metadata hash with every example and group, which
165
+ # contains like their descriptions, the locations at which they were
166
+ # declared, etc, etc. This hash powers many of rspec-core's features,
167
+ # including output formatters (which access descriptions and locations),
168
+ # and filtering before and after hooks.
169
+ #
170
+ # Although you probably won't ever need this unless you are writing an
171
+ # extension, you can access it from an example like this:
172
+ #
173
+ # it "does something" do
174
+ # example.metadata[:description].should eq("does something")
175
+ # end
176
+ #
177
+ # ### `described_class`
178
+ #
179
+ # When a class is passed to `describe`, you can access it from an example
180
+ # using the `described_class` method, which is a wrapper for
181
+ # `example.metadata[:described_class]`.
182
+ #
183
+ # describe Widget do
184
+ # example do
185
+ # described_class.should equal(Widget)
186
+ # end
187
+ # end
188
+ #
189
+ # This is useful in extensions or shared example groups in which the specific
190
+ # class is unknown. Taking the shared examples example from above, we can
191
+ # clean it up a bit using `described_class`:
192
+ #
193
+ # shared_examples "collections" do
194
+ # it "is empty when first created" do
195
+ # described.new.should be_empty
196
+ # end
197
+ # end
198
+ #
199
+ # describe Array do
200
+ # include_examples "collections"
201
+ # end
202
+ #
203
+ # describe Hash do
204
+ # include_examples "collections"
205
+ # end
206
+ #
207
+ # ## The `rspec` command
208
+ #
209
+ # When you install the rspec-core gem, it installs the `rspec` executable,
210
+ # which you'll use to run rspec. The `rspec` comes with many useful options.
211
+ # Run `rspec --help` to see the complete list.
212
+ module Core
213
+ end
78
214
  end
79
215
 
80
216
  require 'rspec/core/backward_compatibility'
@@ -18,10 +18,10 @@ module RSpec
18
18
  @configuration.load_spec_files
19
19
  @world.announce_filters
20
20
 
21
- @configuration.reporter.report(@world.example_count) do |reporter|
21
+ @configuration.reporter.report(@world.example_count, @configuration.randomize? ? @configuration.seed : nil) do |reporter|
22
22
  begin
23
23
  @configuration.run_hook(:before, :suite)
24
- @world.example_groups.map {|g| g.run(reporter)}.all? ? 0 : @configuration.failure_exit_code
24
+ @world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : @configuration.failure_exit_code
25
25
  ensure
26
26
  @configuration.run_hook(:after, :suite)
27
27
  end
@@ -3,51 +3,81 @@ require 'fileutils'
3
3
 
4
4
  module RSpec
5
5
  module Core
6
+ # Stores runtime configuration information.
7
+ #
8
+ # @example Standard settings
9
+ # RSpec.configure do |c|
10
+ # c.drb_port = 1234
11
+ # end
12
+ #
13
+ # @example Hooks
14
+ # RSpec.configure do |c|
15
+ # c.before(:suite) { establish_connection }
16
+ # c.before(:each) { log_in_as :authorized }
17
+ # c.around(:each) { |ex| Database.transaction(&ex) }
18
+ # end
19
+ #
20
+ # @see RSpec.configure
21
+ # @see Hooks
6
22
  class Configuration
7
23
  include RSpec::Core::Hooks
8
24
 
9
25
  class MustBeConfiguredBeforeExampleGroupsError < StandardError; end
10
26
 
27
+ def self.define_predicate_for(*names)
28
+ names.each {|name| alias_method "#{name}?", name}
29
+ end
30
+
31
+ # @api private
32
+ #
33
+ # Invoked by the `add_setting` instance method. Use that method on a
34
+ # `Configuration` instance rather than this class method.
11
35
  def self.add_setting(name, opts={})
36
+ raise "Use the instance add_setting method if you want to set a default" if opts.has_key?(:default)
12
37
  if opts[:alias]
38
+ RSpec.warn_deprecation <<-MESSAGE
39
+ The :alias option to add_setting is deprecated. Use :alias_with on the original setting instead.
40
+ Called from #{caller(0)[4]}
41
+ MESSAGE
13
42
  alias_method name, opts[:alias]
14
43
  alias_method "#{name}=", "#{opts[:alias]}="
15
- alias_method "#{name}?", "#{opts[:alias]}?"
44
+ define_predicate_for name
16
45
  else
17
- define_method("#{name}=") {|val| settings[name] = val}
18
- define_method(name) { settings.has_key?(name) ? settings[name] : opts[:default] }
19
- define_method("#{name}?") { send name }
46
+ attr_writer name
47
+ eval <<-CODE
48
+ def #{name}
49
+ value_for(#{name.inspect}, defined?(@#{name}) ? @#{name} : nil)
50
+ end
51
+ CODE
52
+ define_predicate_for name
53
+ end
54
+ if opts[:alias_with]
55
+ [opts[:alias_with]].flatten.each do |alias_name|
56
+ alias_method alias_name, name
57
+ alias_method "#{alias_name}=", "#{name}="
58
+ define_predicate_for alias_name
59
+ end
20
60
  end
21
61
  end
22
62
 
23
63
  add_setting :error_stream
24
- add_setting :output_stream
25
- add_setting :output, :alias => :output_stream
26
- add_setting :out, :alias => :output_stream
64
+ add_setting :output_stream, :alias_with => [:output, :out]
27
65
  add_setting :drb
28
66
  add_setting :drb_port
29
67
  add_setting :profile_examples
30
68
  add_setting :fail_fast
31
- add_setting :failure_exit_code, :default => 1
69
+ add_setting :failure_exit_code
32
70
  add_setting :run_all_when_everything_filtered
33
- add_setting :exclusion_filter
34
- add_setting :inclusion_filter
35
- add_setting :filter, :alias => :inclusion_filter
36
- add_setting :pattern, :default => '**/*_spec.rb'
37
- add_setting :filename_pattern, :alias => :pattern
71
+ add_setting :pattern, :alias_with => :filename_pattern
38
72
  add_setting :files_to_run
39
73
  add_setting :include_or_extend_modules
40
74
  add_setting :backtrace_clean_patterns
41
75
  add_setting :tty
42
- add_setting :treat_symbols_as_metadata_keys_with_true_values, :default => false
76
+ add_setting :treat_symbols_as_metadata_keys_with_true_values
43
77
  add_setting :expecting_with_rspec
44
- add_setting :default_path, :default => 'spec'
45
- add_setting :show_failures_in_pending_blocks, :default => false
46
-
47
- CONDITIONAL_FILTERS = {
48
- :if => lambda { |value, metadata| metadata.has_key?(:if) && !value },
49
- :unless => lambda { |value| value }
50
- }
78
+ add_setting :default_path
79
+ add_setting :show_failures_in_pending_blocks
80
+ add_setting :order
51
81
 
52
82
  DEFAULT_BACKTRACE_PATTERNS = [
53
83
  /\/lib\d*\/ruby\//,
@@ -59,92 +89,107 @@ module RSpec
59
89
  ]
60
90
 
61
91
  def initialize
62
- @color_enabled = false
63
- self.include_or_extend_modules = []
64
- self.files_to_run = []
65
- self.backtrace_clean_patterns = DEFAULT_BACKTRACE_PATTERNS.dup
66
- self.exclusion_filter = CONDITIONAL_FILTERS.dup
92
+ @expectation_frameworks = []
93
+ @include_or_extend_modules = []
94
+ @mock_framework = nil
95
+ @files_to_run = []
96
+ @formatters = []
97
+ @color = false
98
+ @pattern = '**/*_spec.rb'
99
+ @failure_exit_code = 1
100
+ @backtrace_clean_patterns = DEFAULT_BACKTRACE_PATTERNS.dup
101
+ @default_path = 'spec'
102
+ @filter_manager = FilterManager.new
103
+ @preferred_options = {}
104
+ @seed = srand % 0xFFFF
105
+ end
106
+
107
+ attr_accessor :filter_manager
108
+
109
+ def force(hash)
110
+ @preferred_options.merge!(hash)
111
+ end
112
+
113
+ def force_include(hash)
114
+ filter_manager.include hash
115
+ end
116
+
117
+ def force_exclude(hash)
118
+ filter_manager.exclude hash
67
119
  end
68
120
 
69
121
  def reset
70
122
  @reporter = nil
71
- @formatters.clear if @formatters
123
+ @formatters.clear
72
124
  end
73
125
 
74
- # :call-seq:
75
- # add_setting(:name)
76
- # add_setting(:name, :default => "default_value")
77
- # add_setting(:name, :alias => :other_setting)
126
+ # @overload add_setting(name)
127
+ # @overload add_setting(name, options_hash)
78
128
  #
79
- # Use this to add custom settings to the RSpec.configuration object.
129
+ # Adds a custom setting to the RSpec.configuration object.
80
130
  #
81
- # RSpec.configuration.add_setting :foo
131
+ # RSpec.configuration.add_setting :foo
82
132
  #
83
- # Creates three methods on the configuration object, a setter, a getter,
84
- # and a predicate:
133
+ # Used internally and by extension frameworks like rspec-rails, so they
134
+ # can add config settings that are domain specific. For example:
135
+ #
136
+ # RSpec.configure do |c|
137
+ # c.add_setting :use_transactional_fixtures,
138
+ # :default => true,
139
+ # :alias_with => :use_transactional_examples
140
+ # end
85
141
  #
86
- # RSpec.configuration.foo=(value)
87
- # RSpec.configuration.foo()
88
- # RSpec.configuration.foo?() # returns true if foo returns anything but nil or false
142
+ # `add_setting` creates three methods on the configuration object, a
143
+ # setter, a getter, and a predicate:
89
144
  #
90
- # Intended for extension frameworks like rspec-rails, so they can add config
91
- # settings that are domain specific. For example:
145
+ # RSpec.configuration.foo=(value)
146
+ # RSpec.configuration.foo
147
+ # RSpec.configuration.foo? # returns true if foo returns anything but nil or false
92
148
  #
93
- # RSpec.configure do |c|
94
- # c.add_setting :use_transactional_fixtures, :default => true
95
- # c.add_setting :use_transactional_examples, :alias => :use_transactional_fixtures
96
- # end
149
+ # ### Options
97
150
  #
98
- # == Options
151
+ # `add_setting` takes an optional hash that supports the keys `:default`
152
+ # and `:alias_with`.
99
153
  #
100
- # +add_setting+ takes an optional hash that supports the following
101
- # keys:
154
+ # Use `:default` to set a default value for the generated getter and
155
+ # predicate methods:
102
156
  #
103
- # :default => "default value"
157
+ # add_setting(:foo, :default => "default value")
104
158
  #
105
- # This sets the default value for the getter and the predicate (which
106
- # will return +true+ as long as the value is not +false+ or +nil+).
159
+ # Use `:alias_with` to alias the setter, getter, and predicate to another
160
+ # name, or names:
107
161
  #
108
- # :alias => :other_setting
162
+ # add_setting(:foo, :alias_with => :bar)
163
+ # add_setting(:foo, :alias_with => [:bar, :baz])
109
164
  #
110
- # Aliases its setter, getter, and predicate, to those for the
111
- # +other_setting+.
112
165
  def add_setting(name, opts={})
113
- self.class.add_setting(name, opts)
114
- end
115
-
116
- def puts(message)
117
- output_stream.puts(message)
118
- end
119
-
120
- def settings
121
- @settings ||= {}
122
- end
123
-
124
- def clear_inclusion_filter
125
- self.inclusion_filter = nil
166
+ default = opts.delete(:default)
167
+ (class << self; self; end).class_eval do
168
+ add_setting(name, opts)
169
+ end
170
+ send("#{name}=", default) if default
126
171
  end
127
172
 
173
+ # Used by formatters to ask whether a backtrace line should be displayed
174
+ # or not, based on the line matching any `backtrace_clean_patterns`.
128
175
  def cleaned_from_backtrace?(line)
129
176
  backtrace_clean_patterns.any? { |regex| line =~ regex }
130
177
  end
131
178
 
132
179
  # Returns the configured mock framework adapter module
133
180
  def mock_framework
134
- settings[:mock_framework] ||= begin
135
- require 'rspec/core/mocking/with_rspec'
136
- RSpec::Core::MockFrameworkAdapter
137
- end
181
+ mock_with :rspec unless @mock_framework
182
+ @mock_framework
138
183
  end
139
184
 
140
185
  # Delegates to mock_framework=(framework)
141
- def mock_with(framework)
142
- self.mock_framework = framework
186
+ def mock_framework=(framework)
187
+ mock_with framework
143
188
  end
144
189
 
145
190
  # Sets the mock framework adapter module.
146
191
  #
147
- # +framework+ can be a Symbol or a Module.
192
+ # `framework` can be a Symbol or a Module.
148
193
  #
149
194
  # Given any of :rspec, :mocha, :flexmock, or :rr, configures the named
150
195
  # framework.
@@ -164,11 +209,10 @@ module RSpec
164
209
  #
165
210
  # teardown_mocks_for_rspec
166
211
  # - called after verify_mocks_for_rspec (even if there are errors)
167
- def mock_framework=(framework)
168
- assert_no_example_groups_defined(:mock_framework)
169
- case framework
212
+ def mock_with(framework)
213
+ framework_module = case framework
170
214
  when Module
171
- settings[:mock_framework] = framework
215
+ framework
172
216
  when String, Symbol
173
217
  require case framework.to_s
174
218
  when /rspec/i
@@ -182,72 +226,87 @@ module RSpec
182
226
  else
183
227
  'rspec/core/mocking/with_absolutely_nothing'
184
228
  end
185
- settings[:mock_framework] = RSpec::Core::MockFrameworkAdapter
186
- else
229
+ RSpec::Core::MockFrameworkAdapter
230
+ end
231
+
232
+ new_name, old_name = [framework_module, @mock_framework].map do |mod|
233
+ mod.respond_to?(:framework_name) ? mod.framework_name : :unnamed
234
+ end
235
+
236
+ unless new_name == old_name
237
+ assert_no_example_groups_defined(:mock_framework)
187
238
  end
239
+
240
+ @mock_framework = framework_module
188
241
  end
189
242
 
190
243
  # Returns the configured expectation framework adapter module(s)
191
244
  def expectation_frameworks
192
- expect_with :rspec unless settings[:expectation_frameworks]
193
- settings[:expectation_frameworks]
245
+ expect_with :rspec if @expectation_frameworks.empty?
246
+ @expectation_frameworks
194
247
  end
195
248
 
196
- # Delegates to expect_with([framework])
249
+ # Delegates to expect_with(framework)
197
250
  def expectation_framework=(framework)
198
- expect_with([framework])
251
+ expect_with(framework)
199
252
  end
200
253
 
201
254
  # Sets the expectation framework module(s).
202
255
  #
203
- # +frameworks+ can be :rspec, :stdlib, or both
256
+ # `frameworks` can be :rspec, :stdlib, or both
204
257
  #
205
258
  # Given :rspec, configures rspec/expectations.
206
259
  # Given :stdlib, configures test/unit/assertions
207
260
  # Given both, configures both
208
261
  def expect_with(*frameworks)
209
- assert_no_example_groups_defined(:expect_with)
210
- settings[:expectation_frameworks] = []
211
- frameworks.each do |framework|
262
+ modules = frameworks.map do |framework|
212
263
  case framework
213
- when Symbol
214
- case framework
215
- when :rspec
216
- require 'rspec/core/expecting/with_rspec'
217
- self.expecting_with_rspec = true
218
- when :stdlib
219
- require 'rspec/core/expecting/with_stdlib'
220
- else
221
- raise ArgumentError, "#{framework.inspect} is not supported"
222
- end
223
- settings[:expectation_frameworks] << RSpec::Core::ExpectationFrameworkAdapter
264
+ when :rspec
265
+ require 'rspec/expectations'
266
+ self.expecting_with_rspec = true
267
+ ::RSpec::Matchers
268
+ when :stdlib
269
+ require 'test/unit/assertions'
270
+ ::Test::Unit::Assertions
271
+ else
272
+ raise ArgumentError, "#{framework.inspect} is not supported"
224
273
  end
225
274
  end
226
- end
227
275
 
228
- def full_backtrace=(true_or_false)
229
- settings[:backtrace_clean_patterns] = true_or_false ? [] : DEFAULT_BACKTRACE_PATTERNS
276
+ if (modules - @expectation_frameworks).any?
277
+ assert_no_example_groups_defined(:expect_with)
278
+ end
279
+
280
+ @expectation_frameworks.clear
281
+ @expectation_frameworks.push(*modules)
230
282
  end
231
283
 
232
- def color_enabled
233
- @color_enabled && output_to_tty?
284
+ def full_backtrace=(true_or_false)
285
+ @backtrace_clean_patterns = true_or_false ? [] : DEFAULT_BACKTRACE_PATTERNS
234
286
  end
235
287
 
236
- def color_enabled?
237
- color_enabled
288
+ def color
289
+ return false unless output_to_tty?
290
+ value_for(:color, @color)
238
291
  end
239
292
 
240
- def color_enabled=(bool)
293
+ def color=(bool)
241
294
  return unless bool
242
- @color_enabled = true
295
+ @color = true
243
296
  if bool && ::RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
244
297
  unless ENV['ANSICON']
245
298
  warn "You must use ANSICON 1.31 or later (http://adoxa.110mb.com/ansicon/) to use colour on Windows"
246
- @color_enabled = false
299
+ @color = false
247
300
  end
248
301
  end
249
302
  end
250
303
 
304
+ # TODO - deprecate color_enabled - probably not until the last 2.x
305
+ # release before 3.0
306
+ alias_method :color_enabled, :color
307
+ alias_method :color_enabled=, :color=
308
+ define_predicate_for :color_enabled, :color
309
+
251
310
  def libs=(libs)
252
311
  libs.map {|lib| $LOAD_PATH.unshift lib}
253
312
  end
@@ -277,24 +336,26 @@ EOM
277
336
  end
278
337
  end
279
338
 
280
- # Run examples defined on +line_numbers+ in all files to run.
339
+ # Run examples defined on `line_numbers` in all files to run.
281
340
  def line_numbers=(line_numbers)
282
- filter_run({ :line_numbers => line_numbers.map{|l| l.to_i} }, true)
283
- end
284
-
285
- def add_location(file_path, line_numbers)
286
- # Filter locations is a hash of expanded paths to arrays of line numbers
287
- # to match against.
288
- #
289
- filter_locations = ((self.filter || {})[:locations] ||= {})
290
- (filter_locations[File.expand_path(file_path)] ||= []).push(*line_numbers)
291
- filter_run({ :locations => filter_locations })
341
+ filter_run :line_numbers => line_numbers.map{|l| l.to_i}
292
342
  end
293
343
 
294
344
  def full_description=(description)
295
- filter_run({ :full_description => /#{description}/ }, true)
345
+ filter_run :full_description => /#{description}/
296
346
  end
297
347
 
348
+ # @overload add_formatter(formatter)
349
+ #
350
+ # Adds a formatter to the formatters collection. `formatter` can be a
351
+ # string representing any of the built-in formatters (see
352
+ # `built_in_formatter`), or a custom formatter class.
353
+ #
354
+ # ### Note
355
+ #
356
+ # For internal purposes, `add_formatter` also accepts the name of a class
357
+ # and path to a file that contains that class definition, but you should
358
+ # consider that a private api that may change at any time without notice.
298
359
  def add_formatter(formatter_to_use, path=nil)
299
360
  formatter_class =
300
361
  built_in_formatter(formatter_to_use) ||
@@ -323,10 +384,12 @@ EOM
323
384
  self.files_to_run = get_files_to_run(files)
324
385
  end
325
386
 
387
+ # @api private
326
388
  def command
327
389
  $0.split(File::SEPARATOR).last
328
390
  end
329
391
 
392
+ # @api private
330
393
  def get_files_to_run(files)
331
394
  patterns = pattern.split(",")
332
395
  files.map do |file|
@@ -341,7 +404,7 @@ EOM
341
404
  else
342
405
  if file =~ /^(.*?)((?:\:\d+)+)$/
343
406
  path, lines = $1, $2[1..-1].split(":").map{|n| n.to_i}
344
- add_location path, lines
407
+ filter_manager.add_location path, lines
345
408
  path
346
409
  else
347
410
  file
@@ -350,8 +413,27 @@ EOM
350
413
  end.flatten
351
414
  end
352
415
 
353
- # E.g. alias_example_to :crazy_slow, :speed => 'crazy_slow' defines
354
- # crazy_slow as an example variant that has the crazy_slow speed option
416
+ # Creates a method that delegates to `example` including the submitted
417
+ # `args`. Used internally to add variants of `example` like `pending`:
418
+ #
419
+ # @example
420
+ # alias_example_to :pending, :pending => true
421
+ #
422
+ # # This lets you do this:
423
+ #
424
+ # describe Thing do
425
+ # pending "does something" do
426
+ # thing = Thing.new
427
+ # end
428
+ # end
429
+ #
430
+ # # ... which is the equivalent of
431
+ #
432
+ # describe Thing do
433
+ # it "does something", :pending => true do
434
+ # thing = Thing.new
435
+ # end
436
+ # end
355
437
  def alias_example_to(new_name, *args)
356
438
  extra_options = build_metadata_hash_from(args)
357
439
  RSpec::Core::ExampleGroup.alias_example_to(new_name, extra_options)
@@ -382,50 +464,82 @@ EOM
382
464
  RSpec::Core::ExampleGroup.alias_it_should_behave_like_to(new_name, report_label)
383
465
  end
384
466
 
385
- undef_method :exclusion_filter=
386
- def exclusion_filter=(filter)
387
- settings[:exclusion_filter] = filter
467
+ # Adds key/value pairs to the `inclusion_filter`. If the
468
+ # `treat_symbols_as_metadata_keys_with_true_values` config option is set
469
+ # to true and `args` includes any symbols that are not part of a hash,
470
+ # each symbol is treated as a key in the hash with the value `true`.
471
+ #
472
+ # ### Note
473
+ #
474
+ # Filters set using this method can be overridden from the command line
475
+ # or config files (e.g. `.rspec`).
476
+ #
477
+ # @example
478
+ # filter_run_including :x => 'y'
479
+ #
480
+ # # with treat_symbols_as_metadata_keys_with_true_values = true
481
+ # filter_run_including :foo # results in {:foo => true}
482
+ def filter_run_including(*args)
483
+ filter_manager.include :low_priority, build_metadata_hash_from(args)
388
484
  end
389
485
 
390
- undef_method :exclusion_filter
391
- def exclusion_filter
392
- settings[:exclusion_filter] || {}
393
- end
486
+ alias_method :filter_run, :filter_run_including
394
487
 
488
+ # Clears and reassigns the `inclusion_filter`. Set to `nil` if you don't
489
+ # want any inclusion filter at all.
490
+ #
491
+ # ### Warning
492
+ #
493
+ # This overrides any inclusion filters/tags set on the command line or in
494
+ # configuration files.
395
495
  def inclusion_filter=(filter)
396
- settings[:inclusion_filter] = filter
496
+ filter_manager.include :replace, build_metadata_hash_from([filter])
397
497
  end
398
498
 
499
+ alias_method :filter=, :inclusion_filter=
500
+
501
+ # Returns the `inclusion_filter`. If none has been set, returns an empty
502
+ # hash.
399
503
  def inclusion_filter
400
- settings[:inclusion_filter] || {}
504
+ filter_manager.inclusions
401
505
  end
402
506
 
403
- def filter_run_including(*args)
404
- force_overwrite = if args.last.is_a?(Hash) || args.last.is_a?(Symbol)
405
- false
406
- else
407
- args.pop
408
- end
409
-
410
- options = build_metadata_hash_from(args)
507
+ alias_method :filter, :inclusion_filter
411
508
 
412
- if inclusion_filter and inclusion_filter[:line_numbers] || inclusion_filter[:full_description]
413
- warn "Filtering by #{options.inspect} is not possible since " \
414
- "you are already filtering by #{inclusion_filter.inspect}"
415
- else
416
- if force_overwrite
417
- self.inclusion_filter = options
418
- else
419
- self.inclusion_filter = (inclusion_filter || {}).merge(options)
420
- end
421
- end
509
+ # Adds key/value pairs to the `exclusion_filter`. If the
510
+ # `treat_symbols_as_metadata_keys_with_true_values` config option is set
511
+ # to true and `args` excludes any symbols that are not part of a hash,
512
+ # each symbol is treated as a key in the hash with the value `true`.
513
+ #
514
+ # ### Note
515
+ #
516
+ # Filters set using this method can be overridden from the command line
517
+ # or config files (e.g. `.rspec`).
518
+ #
519
+ # @example
520
+ # filter_run_excluding :x => 'y'
521
+ #
522
+ # # with treat_symbols_as_metadata_keys_with_true_values = true
523
+ # filter_run_excluding :foo # results in {:foo => true}
524
+ def filter_run_excluding(*args)
525
+ filter_manager.exclude :low_priority, build_metadata_hash_from(args)
422
526
  end
423
527
 
424
- alias_method :filter_run, :filter_run_including
528
+ # Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't
529
+ # want any exclusion filter at all.
530
+ #
531
+ # ### Warning
532
+ #
533
+ # This overrides any exclusion filters/tags set on the command line or in
534
+ # configuration files.
535
+ def exclusion_filter=(filter)
536
+ filter_manager.exclude :replace, build_metadata_hash_from([filter])
537
+ end
425
538
 
426
- def filter_run_excluding(*args)
427
- options = build_metadata_hash_from(args)
428
- self.exclusion_filter = (exclusion_filter || {}).merge(options)
539
+ # Returns the `exclusion_filter`. If none has been set, returns an empty
540
+ # hash.
541
+ def exclusion_filter
542
+ filter_manager.exclusions
429
543
  end
430
544
 
431
545
  def include(mod, *args)
@@ -438,6 +552,10 @@ EOM
438
552
  include_or_extend_modules << [:extend, mod, filters]
439
553
  end
440
554
 
555
+ # @api private
556
+ #
557
+ # Used internally to extend a group with modules using `include` and/or
558
+ # `extend`.
441
559
  def configure_group(group)
442
560
  include_or_extend_modules.each do |include_or_extend, mod, filters|
443
561
  next unless filters.empty? || group.any_apply?(filters)
@@ -460,8 +578,35 @@ EOM
460
578
  raise_if_rspec_1_is_loaded
461
579
  end
462
580
 
581
+ attr_reader :seed
582
+
583
+ def seed=(seed)
584
+ @order = 'rand'
585
+ @seed = seed.to_i
586
+ end
587
+
588
+ def randomize?
589
+ order.to_s.match(/rand/)
590
+ end
591
+
592
+ remove_method :order=
593
+ def order=(type)
594
+ order, seed = type.to_s.split(':')
595
+ if order == 'default'
596
+ @order = nil
597
+ @seed = nil
598
+ else
599
+ @order = order
600
+ @seed = seed.to_i if seed
601
+ end
602
+ end
603
+
463
604
  private
464
605
 
606
+ def value_for(key, default=nil)
607
+ @preferred_options.has_key?(key) ? @preferred_options[key] : default
608
+ end
609
+
465
610
  def assert_no_example_groups_defined(config_option)
466
611
  if RSpec.world.example_groups.any?
467
612
  raise MustBeConfiguredBeforeExampleGroupsError.new(
@@ -524,7 +669,7 @@ MESSAGE
524
669
  end
525
670
  end
526
671
  end
527
-
672
+
528
673
  def string_const?(str)
529
674
  str.is_a?(String) && /\A[A-Z][a-zA-Z0-9_:]*\z/ =~ str
530
675
  end