rspec-core 2.7.1 → 2.8.0.rc1

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 (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