Skip to content
Browse files

Merge branch 'master' of git://github.com/rspec/rspec-core

  • Loading branch information...
2 parents a89fb4d + 454f3b4 commit db5a740c017068b224969bf0cf9b0e325b7012b9 Ivan Neverov committed Jan 16, 2012
Showing with 1,589 additions and 1,013 deletions.
  1. +3 −3 .document
  2. +2 −0 .gitignore
  3. +0 −1 .rspec
  4. +2 −1 .travis.yml
  5. +0 −4 .yardopts
  6. +24 −2 Changelog.md
  7. +28 −0 DEV-README.md
  8. +4 −46 Gemfile
  9. +36 −0 Gemfile-custom.sample
  10. +116 −85 README.md
  11. +5 −1 Rakefile
  12. +3 −3 features/command_line/format_option.feature
  13. +1 −1 features/example_groups/shared_examples.feature
  14. +4 −4 features/hooks/around_hooks.feature
  15. +5 −5 features/pending/pending_examples.feature
  16. +8 −1 features/support/env.rb
  17. +6 −6 lib/autotest/rspec2.rb
  18. +10 −8 lib/rspec/core.rb
  19. +2 −0 lib/rspec/core/backward_compatibility.rb
  20. +4 −0 lib/rspec/core/command_line.rb
  21. +235 −115 lib/rspec/core/configuration.rb
  22. +5 −1 lib/rspec/core/configuration_options.rb
  23. +2 −3 lib/rspec/core/deprecation.rb
  24. +71 −58 lib/rspec/core/drb_options.rb
  25. +13 −0 lib/rspec/core/dsl.rb
  26. +0 −13 lib/rspec/core/errors.rb
  27. +23 −17 lib/rspec/core/example.rb
  28. +82 −27 lib/rspec/core/example_group.rb
  29. +5 −0 lib/rspec/core/extensions/instance_eval_with_args.rb
  30. +1 −1 lib/rspec/core/extensions/kernel.rb
  31. +4 −0 lib/rspec/core/extensions/module_eval_with_args.rb
  32. +7 −2 lib/rspec/core/extensions/ordered.rb
  33. +68 −34 lib/rspec/core/filter_manager.rb
  34. +28 −21 lib/rspec/core/formatters/base_text_formatter.rb
  35. +1 −2 lib/rspec/core/formatters/html_formatter.rb
  36. +96 −35 lib/rspec/core/hooks.rb
  37. +58 −57 lib/rspec/core/let.rb
  38. +97 −79 lib/rspec/core/metadata.rb
  39. +4 −0 lib/rspec/core/metadata_hash_builder.rb
  40. +37 −44 lib/rspec/core/option_parser.rb
  41. +25 −3 lib/rspec/core/pending.rb
  42. +22 −3 lib/rspec/core/project_initializer.rb
  43. +2 −2 lib/rspec/core/runner.rb
  44. +1 −3 lib/rspec/core/shared_context.rb
  45. +1 −1 lib/rspec/core/version.rb
  46. +2 −0 lib/rspec/monkey/spork/test_framework/rspec.rb
  47. +15 −3 rspec-core.gemspec
  48. +19 −13 spec/command_line/order_spec.rb
  49. +0 −3 spec/rspec/core/command_line_spec.rb
  50. +17 −18 spec/rspec/core/configuration_options_spec.rb
  51. +28 −14 spec/rspec/core/configuration_spec.rb
  52. +20 −37 spec/rspec/core/drb_command_line_spec.rb
  53. +51 −3 spec/rspec/core/drb_options_spec.rb
  54. +18 −12 spec/rspec/core/example_group_spec.rb
  55. +14 −0 spec/rspec/core/example_spec.rb
  56. +114 −33 spec/rspec/core/filter_manager_spec.rb
  57. +17 −69 spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html
  58. +2 −2 spec/rspec/core/formatters/html_formatted-1.8.7.html
  59. +2 −2 spec/rspec/core/formatters/html_formatted-1.9.2.html
  60. +2 −2 spec/rspec/core/formatters/html_formatted-1.9.3.html
  61. +20 −72 spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html
  62. +2 −2 spec/rspec/core/formatters/text_mate_formatted-1.8.7.html
  63. +2 −2 spec/rspec/core/formatters/text_mate_formatted-1.9.2.html
  64. +2 −2 spec/rspec/core/formatters/text_mate_formatted-1.9.3.html
  65. +38 −0 spec/rspec/core/metadata_spec.rb
  66. +5 −0 spec/rspec/core/option_parser_spec.rb
  67. +44 −12 spec/rspec/core/pending_example_spec.rb
  68. +2 −2 spec/rspec/core/project_initializer_spec.rb
  69. +0 −6 spec/rspec/core_spec.rb
  70. +2 −12 spec/support/matchers.rb
View
6 .document
@@ -1,5 +1,5 @@
-README.markdown
lib/**/*.rb
-bin/*
-features/**/*.feature
+-
+README.md
License.txt
+Changelog.md
View
2 .gitignore
@@ -14,3 +14,5 @@ Gemfile.lock
bin
.rbx
.yardoc
+vendor
+Gemfile-custom
View
1 .rspec
@@ -1,3 +1,2 @@
--default_path spec
--order rand
---tag ~ui
View
3 .travis.yml
@@ -1,7 +1,8 @@
script: "bin/rake --trace 2>&1"
-bundler_args: "--binstubs --without development"
+bundler_args: "--binstubs"
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- ree
+ - jruby
View
4 .yardopts
@@ -1,7 +1,3 @@
--no-private
--exclude features
--markup markdown
--
-README.md
-Changelog.md
-License.txt
View
26 Changelog.md
@@ -1,17 +1,39 @@
### dev
-[full changelog](http://github.com/rspec/rspec-core/compare/v2.8.0.rc1...master)
+Bug fixes
+
+* Restore `--full_backtrace` option
+
+### 2.8.0 / 2012-01-04
+
+[full changelog](http://github.com/rspec/rspec-core/compare/v2.8.0.rc2...v2.8.0)
+
+Bug fixes
+
+* For metadata filtering, restore passing the entire array to the proc, rather
+ than each item in the array (weidenfreak)
+* Ensure each spec file is loaded only once
+ * Fixes a bug that caused all the examples in a file to be run when
+ referenced twice with line numbers in a command, e.g.
+ * `rspec path/to/file:37 path/to/file:42`
+
+### 2.8.0.rc2 / 2011-12-19
+
+[full changelog](http://github.com/rspec/rspec-core/compare/v2.8.0.rc1...v2.8.0.rc2)
Enhancments
* new `--init` command (Peter Schröder)
* generates `spec/spec_helper.rb`
- * deletes obsolete files (on confirmation)
+ * deletes obsolete files (on confirmation)
* merged with and deprecates `--configure` command, which generated
`.rspec`
* use `require_relative` when available (Ian Leitch)
* `include_context` and `include_examples` accept params (Calvin Bascom)
* print the time for every example in the html formatter (Richie Vos)
+* several tasty refactoring niblets (Sasha)
+* `it "does something", :x => [:foo,'bar',/baz/] (Ivan Neverov)
+ * supports matching n command line tag values with an example or group
### 2.8.0.rc1 / 2011-11-06
View
28 DEV-README.md
@@ -0,0 +1,28 @@
+## Set up the dev environment
+
+ git clone git://github.com/rspec/rspec-core.git
+ cd rspec-core
+ gem install bundler
+ bundle install
+
+Now you should be able to run any of:
+
+ rake
+ rake spec
+ rake cucumber
+
+Or, if you prefer to use the rspec and cucumber commands directly, you can either:
+
+ bundle exec rspec
+
+Or ...
+
+ bundle install --binstubs
+ bin/rspec
+
+## Customize the dev enviroment
+
+The Gemfile includes the gems you'll need to be able to run specs. If you want
+to customize your dev enviroment with additional tools like guard or
+ruby-debug, add any additional gem declarations to Gemfile-custom (see
+Gemfile-custom.sample for some examples).
View
50 Gemfile
@@ -1,7 +1,9 @@
source "http://rubygems.org"
+gemspec
+
### rspec libs
-%w[rspec rspec-core rspec-expectations rspec-mocks].each do |lib|
+%w[rspec-core rspec-expectations rspec-mocks].each do |lib|
library_path = File.expand_path("../../#{lib}", __FILE__)
if File.exist?(library_path)
gem lib, :path => library_path
@@ -10,52 +12,8 @@ source "http://rubygems.org"
end
end
-### dev dependencies
-gem "rake", "0.9.2"
-gem "cucumber", "1.0.1"
-gem "aruba", "0.4.2"
-gem "ZenTest", "4.6.2"
-gem "nokogiri", "1.5.0"
-gem "fakefs", "0.4.0", :require => "fakefs/safe"
-
platforms :jruby do
gem "jruby-openssl"
end
-### rspec-core only
-gem "mocha", "~> 0.9.10"
-gem "rr", "~> 1.0.2"
-gem "flexmock", "0.8.8"
-
-### optional runtime deps
-gem "syntax", "1.0.0"
-
-group :development do
- gem 'interactive_rspec'
- gem 'yard'
- gem "relish", "~> 0.5.0"
- gem "guard-rspec", "0.5.0"
- gem "growl", "1.0.3"
- gem "spork", "0.9.0.rc9"
-
- platforms :mri_18, :jruby do
- gem "rcov", "0.9.10"
- end
-
- platforms :mri_18 do
- gem 'ruby-debug'
- end
-
- platforms :mri_19 do
- if RUBY_VERSION == '1.9.2'
- gem 'linecache19', '~> 0.5.12'
- gem 'ruby-debug19', '~> 0.11.6'
- gem 'ruby-debug-base19', '~> 0.11.25'
- end
- end
-
- platforms :mri_18, :mri_19 do
- gem "rb-fsevent", "~> 0.4.3.1"
- gem "ruby-prof", "~> 0.10.0"
- end
-end
+eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom')
View
36 Gemfile-custom.sample
@@ -0,0 +1,36 @@
+group :development do
+ gem 'interactive_rspec'
+ gem 'yard', "~> 0.7.4"
+ gem "relish", "~> 0.5.0"
+ gem "guard-rspec", "0.5.0"
+ gem "growl", "1.0.3"
+ gem "spork", "0.9.0.rc9"
+
+ platforms :mri_18, :jruby do
+ gem "rcov", "0.9.10"
+ end
+
+ platforms :mri_18 do
+ gem 'ruby-debug'
+ end
+
+ platforms :mri_19 do
+ case RUBY_VERSION
+ when '1.9.2'
+ gem 'ruby-debug19', '0.11.6'
+ gem 'ruby-debug-base19', '0.11.25'
+ gem 'linecache19', '0.5.12'
+ when '1.9.3'
+ # NOTE - see http://blog.wyeworks.com/2011/11/1/ruby-1-9-3-and-ruby-debug
+ # for help setting up ruby-debug19 on ruby 1.9.3
+ gem 'ruby-debug19', '0.11.6'
+ gem 'ruby-debug-base19', '0.11.26'
+ gem 'linecache19', '0.5.13'
+ end
+ end
+
+ platforms :mri_18, :mri_19 do
+ gem "rb-fsevent", "~> 0.4.3.1"
+ gem "ruby-prof", "~> 0.10.0"
+ end
+end
View
201 README.md
@@ -1,5 +1,12 @@
# rspec-core
+```
+gem install rspec # for rspec-core, rspec-expectations, rspec-mocks
+gem install rspec-core # for rspec-core only
+```
+
+# overview
+
rspec-core provides the structure for writing executable examples of how
your code should behave. It uses the words "describe" and "it" so we can
express concepts like a conversation:
@@ -9,19 +16,21 @@ express concepts like a conversation:
## basic structure
- describe Order do
- it "sums the prices of its line items" do
- order = Order.new
- order.add_entry(LineItem.new(:item => Item.new(
- :price => Money.new(1.11, :USD)
- )
- order.add_entry(LineItem.new(:item => Item.new(
- :price => Money.new(2.22, :USD),
- :quantity => 2
- )
- order.total.should eq(Money.new(5.55, :USD))
- end
- end
+```ruby
+describe Order do
+ it "sums the prices of its line items" do
+ order = Order.new
+ order.add_entry(LineItem.new(:item => Item.new(
+ :price => Money.new(1.11, :USD)
+ )))
+ order.add_entry(LineItem.new(:item => Item.new(
+ :price => Money.new(2.22, :USD),
+ :quantity => 2
+ )))
+ order.total.should eq(Money.new(5.55, :USD))
+ end
+end
+```
The `describe` method creates an [ExampleGroup](../RSpec/Core/ExampleGroup). Within the
block passed to `describe` you can declare examples using the `it` method.
@@ -35,19 +44,21 @@ context of an _instance_ of that class.
You can also declare nested nested groups using the `describe` or `context`
methods:
- describe Order to
- context "with no items" do
- it "behaves one way" do
- # ...
- end
- end
-
- context "with one item" do
- it "behaves another way" do
- # ...
- end
- end
+```ruby
+describe Order do
+ context "with no items" do
+ it "behaves one way" do
+ # ...
+ end
+ end
+
+ context "with one item" do
+ it "behaves another way" do
+ # ...
end
+ end
+end
+```
## aliases
@@ -62,19 +73,21 @@ You can declare examples within a group using any of `it`, `specify`, or
Declare a shared example group using `shared_examples`, and then include it
in any group using `include_examples`.
- shared_examples "collections" do |collection_class|
- it "is empty when first created" do
- collection_class.new.should be_empty
- end
- end
+```ruby
+shared_examples "collections" do |collection_class|
+ it "is empty when first created" do
+ collection_class.new.should be_empty
+ end
+end
- describe Array do
- include_examples "collections", Array
- end
+describe Array do
+ include_examples "collections", Array
+end
- describe Hash do
- include_examples "collections", Hash
- end
+describe Hash do
+ include_examples "collections", Hash
+end
+```
Nearly anything that can be declared within an example group can be declared
within a shared example group. This includes `before`, `after`, and `around`
@@ -96,39 +109,45 @@ and filtering before and after hooks.
Although you probably won't ever need this unless you are writing an
extension, you can access it from an example like this:
- it "does something" do
- example.metadata[:description].should eq("does something")
- end
+```ruby
+it "does something" do
+ example.metadata[:description].should eq("does something")
+end
+```
### `described_class`
When a class is passed to `describe`, you can access it from an example
using the `described_class` method, which is a wrapper for
`example.metadata[:described_class]`.
- describe Widget do
- example do
- described_class.should equal(Widget)
- end
- end
+```ruby
+describe Widget do
+ example do
+ described_class.should equal(Widget)
+ end
+end
+```
This is useful in extensions or shared example groups in which the specific
class is unknown. Taking the shared examples example from above, we can
clean it up a bit using `described_class`:
- shared_examples "collections" do
- it "is empty when first created" do
- described.new.should be_empty
- end
- end
+```ruby
+shared_examples "collections" do
+ it "is empty when first created" do
+ described.new.should be_empty
+ end
+end
- describe Array do
- include_examples "collections"
- end
+describe Array do
+ include_examples "collections"
+end
- describe Hash do
- include_examples "collections"
- end
+describe Hash do
+ include_examples "collections"
+end
+```
## the `rspec` command
@@ -146,52 +165,64 @@ Run `rspec --help` to see the complete list.
Start with a simple example of behavior you expect from your system. Do
this before you write any implementation code:
- # in spec/calculator_spec.rb
- describe Calculator do
- it "add(x,y) returns the sum of its arguments" do
- Calculator.new.add(1, 2).should eq(3)
- end
- end
+```ruby
+# in spec/calculator_spec.rb
+describe Calculator do
+ it "add(x,y) returns the sum of its arguments" do
+ Calculator.new.add(1, 2).should eq(3)
+ end
+end
+```
Run this with the rspec command, and watch it fail:
- $ rspec spec/calculator_spec.rb
- ./spec/calculator_spec.rb:1: uninitialized constant Calculator
+```
+$ rspec spec/calculator_spec.rb
+./spec/calculator_spec.rb:1: uninitialized constant Calculator
+```
Implement the simplest solution:
- # in lib/calculator.rb
- class Calculator
- def add(a,b)
- a + b
- end
- end
+```ruby
+# in lib/calculator.rb
+class Calculator
+ def add(a,b)
+ a + b
+ end
+end
+```
Be sure to require the implementation file in the spec:
- # in spec/calculator_spec.rb
- # - RSpec adds ./lib to the $LOAD_PATH
- require "calculator"
+```ruby
+# in spec/calculator_spec.rb
+# - RSpec adds ./lib to the $LOAD_PATH
+require "calculator"
+```
Now run the spec again, and watch it pass:
- $ rspec spec/calculator_spec.rb
- .
+```
+$ rspec spec/calculator_spec.rb
+.
- Finished in 0.000315 seconds
- 1 example, 0 failures
+Finished in 0.000315 seconds
+1 example, 0 failures
+```
Use the `documentation` formatter to see the resulting spec:
- $ rspec spec/calculator_spec.rb --format doc
- Calculator add
- returns the sum of its arguments
+```
+$ rspec spec/calculator_spec.rb --format doc
+Calculator add
+ returns the sum of its arguments
- Finished in 0.000379 seconds
- 1 example, 0 failures
+Finished in 0.000379 seconds
+1 example, 0 failures
+```
-## install
-
- gem install rspec # for rspec-core, rspec-expectations, rspec-mocks
- gem install rspec-core # for rspec-core only
+## Also see
+* [http://github.com/rspec/rspec](http://github.com/rspec/rspec)
+* [http://github.com/rspec/rspec-expectations](http://github.com/rspec/rspec-expectations)
+* [http://github.com/rspec/rspec-mocks](http://github.com/rspec/rspec-mocks)
View
6 Rakefile
@@ -1,5 +1,4 @@
require "bundler"
-Bundler.setup
Bundler::GemHelper.install_tasks
task :build => :raise_if_psych_is_defined
@@ -68,6 +67,11 @@ task :clobber do
sh 'rm -rf doc'
end
+desc "generate rdoc"
+task :rdoc do
+ sh "yardoc"
+end
+
desc "Push docs/cukes to relishapp using the relish-client-gem"
task :relish, :version do |t, args|
raise "rake relish[VERSION]" unless args[:version]
View
6 features/command_line/format_option.feature
@@ -56,7 +56,7 @@ Feature: --format option
something
does something that passes
does something that fails (FAILED - 1)
- does something that is pending (PENDING: Not Yet Implemented)
+ does something that is pending (PENDING: No reason given)
"""
Scenario: documentation format saved to a file
@@ -66,7 +66,7 @@ Feature: --format option
something
does something that passes
does something that fails (FAILED - 1)
- does something that is pending (PENDING: Not Yet Implemented)
+ does something that is pending (PENDING: No reason given)
"""
Scenario: multiple formats
@@ -77,5 +77,5 @@ Feature: --format option
something
does something that passes
does something that fails (FAILED - 1)
- does something that is pending (PENDING: Not Yet Implemented)
+ does something that is pending (PENDING: No reason given)
"""
View
2 features/example_groups/shared_examples.feature
@@ -27,7 +27,7 @@ Feature: shared examples
2. Put files containing shared examples in `spec/support/` and require files
in that directory from `spec/spec_helper.rb`:
- Dir["spec/support/**/*.rb"].each {|f| require f}
+ Dir["./spec/support/**/*.rb"].each {|f| require f}
This is included in the generated `spec/spec_helper.rb` file in
`rspec-rails`
View
8 features/hooks/around_hooks.feature
@@ -199,24 +199,24 @@ Feature: around hooks
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failure"
- Scenario: implicitly pending examples are detected as Not Yet Implemented
+ Scenario: implicitly pending examples are detected as Not yet implemented
Given a file named "example_spec.rb" with:
"""
describe "implicit pending example" do
around(:each) do |example|
example.run
end
- it "should be detected as Not Yet Implemented"
+ it "should be detected as Not yet implemented"
end
"""
When I run `rspec example_spec.rb`
Then the output should contain "1 example, 0 failures, 1 pending"
And the output should contain:
"""
Pending:
- implicit pending example should be detected as Not Yet Implemented
- # Not Yet Implemented
+ implicit pending example should be detected as Not yet implemented
+ # Not yet implemented
"""
View
10 features/pending/pending_examples.feature
@@ -13,7 +13,7 @@ Feature: pending examples
When I run `rspec example_without_block_spec.rb`
Then the exit status should be 0
And the output should contain "1 example, 0 failures, 1 pending"
- And the output should contain "Not Yet Implemented"
+ And the output should contain "Not yet implemented"
And the output should contain "example_without_block_spec.rb:2"
Scenario: pending any arbitary reason, with no block
@@ -98,13 +98,13 @@ Feature: pending examples
"""
Pending:
an example is pending using xit
- # Not Yet Implemented
+ # Temporarily disabled with xit
# ./temporarily_pending_spec.rb:2
an example is pending using xspecify
- # Not Yet Implemented
+ # Temporarily disabled with xspecify
# ./temporarily_pending_spec.rb:5
an example is pending using xexample
- # Not Yet Implemented
+ # Temporarily disabled with xexample
# ./temporarily_pending_spec.rb:8
"""
@@ -149,7 +149,7 @@ Feature: pending examples
"""
an example
checks something
- (PENDING: Not Yet Implemented)
+ (PENDING: No reason given)
"""
Scenario: conditionally pending examples
View
9 features/support/env.rb
@@ -1,5 +1,12 @@
require 'aruba/cucumber'
Before do
- RUBY_PLATFORM =~ /java/ ? @aruba_timeout_seconds = 60 : @aruba_timeout_seconds = 5
+ if RUBY_PLATFORM =~ /java/
+ # ideas taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html
+ set_env('JRUBY_OPTS', '-X-C') # disable JIT since these processes are so short lived
+ set_env('JAVA_OPTS', '-d32') # force jRuby to use client JVM for faster startup times
+ @aruba_timeout_seconds = 60
+ else
+ @aruba_timeout_seconds = 5
+ end
end
View
12 lib/autotest/rspec2.rb
@@ -1,8 +1,10 @@
require 'autotest'
require 'rspec/core/deprecation'
-class RSpecCommandError < StandardError; end
-
+# Derived from the `Autotest` class, extends the `autotest` command to work
+# with RSpec.
+#
+# @note this will be extracted to a separate gem when we release rspec-3.
class Autotest::Rspec2 < Autotest
RSPEC_EXECUTABLE = File.expand_path('../../../exe/rspec', __FILE__)
@@ -55,19 +57,17 @@ def normalize(files_to_test)
end
end
- # @private
+ private
+
def suffix
using_bundler? ? "" : defined?(:Gem) ? " -rrubygems" : ""
end
- # @private
def using_bundler?
prefix =~ /bundle exec/
end
- # @private
def gemfile?
File.exist?('./Gemfile')
end
-
end
View
18 lib/rspec/core.rb
@@ -1,8 +1,10 @@
if defined?(require_relative)
+ # @private
def require_rspec(path)
require_relative path
end
else
+ # @private
def require_rspec(path)
require "rspec/#{path}"
end
@@ -37,39 +39,38 @@ def require_rspec(path)
require_rspec 'core/shared_example_group'
require_rspec 'core/example_group'
require_rspec 'core/version'
-require_rspec 'core/errors'
module RSpec
autoload :Matchers, 'rspec/matchers'
autoload :SharedContext, 'rspec/core/shared_context'
- # @api private
- # Used internally to determine what to do when a SIGINT is received
+ # @private
def self.wants_to_quit
+ # Used internally to determine what to do when a SIGINT is received
world.wants_to_quit
end
- # @api private
+ # @private
# Used internally to determine what to do when a SIGINT is received
def self.wants_to_quit=(maybe)
world.wants_to_quit=(maybe)
end
- # @api private
+ # @private
# Internal container for global non-configuration data
def self.world
@world ||= RSpec::Core::World.new
end
- # @api private
+ # @private
# Used internally to ensure examples get reloaded between multiple runs in
# the same process.
def self.reset
world.reset
configuration.reset
end
- # Returns the global [Configuration](Core/Configuration) object. While you
+ # Returns the global [Configuration](RSpec/Core/Configuration) object. While you
# _can_ use this method to access the configuration, the more common
# convention is to use [RSpec.configure](RSpec#configure-class_method).
#
@@ -81,6 +82,7 @@ def self.configuration
@configuration ||= RSpec::Core::Configuration.new
end
+ # Yields the global configuration to a block.
# @yield [Configuration] global configuration
#
# @example
@@ -92,7 +94,7 @@ def self.configure
yield configuration if block_given?
end
- # @api private
+ # @private
# Used internally to clear remaining groups when fail_fast is set
def self.clear_remaining_example_groups
world.example_groups.clear
View
2 lib/rspec/core/backward_compatibility.rb
@@ -1,5 +1,6 @@
module RSpec
module Core
+ # @private
module ConstMissing
# Used to print deprecation warnings for Rspec and Spec constants (use
# RSpec instead)
@@ -38,6 +39,7 @@ def self.configure(&block)
end
end
+ # @private
module Rake
# Used to print deprecation warnings for Rake::SpecTask constant (use
# RSpec::Core::RakeTask instead)
View
4 lib/rspec/core/command_line.rb
@@ -11,6 +11,10 @@ def initialize(options, configuration=RSpec::configuration, world=RSpec::world)
@world = world
end
+ # Configures and runs a suite
+ #
+ # @param [IO] err
+ # @param [IO] out
def run(err, out)
@configuration.error_stream = err
@configuration.output_stream ||= out
View
350 lib/rspec/core/configuration.rb
@@ -7,7 +7,9 @@ module Core
#
# @example Standard settings
# RSpec.configure do |c|
- # c.drb_port = 1234
+ # c.drb = true
+ # c.drb_port = 1234
+ # c.default_path = 'behavior'
# end
#
# @example Hooks
@@ -24,7 +26,7 @@ class Configuration
class MustBeConfiguredBeforeExampleGroupsError < StandardError; end
- # @api private
+ # @private
def self.define_reader(name)
eval <<-CODE
def #{name}
@@ -33,27 +35,27 @@ def #{name}
CODE
end
- # @api private
+ # @private
def self.deprecate_alias_key
RSpec.warn_deprecation <<-MESSAGE
The :alias option to add_setting is deprecated. Use :alias_with on the original setting instead.
Called from #{caller(0)[5]}
MESSAGE
end
- # @api private
+ # @private
def self.define_aliases(name, alias_name)
alias_method alias_name, name
alias_method "#{alias_name}=", "#{name}="
define_predicate_for alias_name
end
- # @api private
+ # @private
def self.define_predicate_for(*names)
names.each {|name| alias_method "#{name}?", name}
end
- # @api private
+ # @private
#
# Invoked by the `add_setting` instance method. Use that method on a
# `Configuration` instance rather than this class method.
@@ -72,25 +74,99 @@ def self.add_setting(name, opts={})
end
end
- add_setting :error_stream
- add_setting :output_stream, :alias_with => [:output, :out]
+ # @macro [attach] add_setting
+ # @attribute $1
+ # Patterns to match against lines in backtraces presented in failure
+ # messages in order to filter them out (default:
+ # DEFAULT_BACKTRACE_PATTERNS). You can either replace this list using
+ # the setter or modify it using the getter.
+ #
+ # To override this behavior and display a full backtrace, use
+ # `--backtrace` on the command line, in a `.rspec` file, or in the
+ # `rspec_options` attribute of RSpec's rake task.
+ add_setting :backtrace_clean_patterns
+
+ # Path to use if no path is provided to the `rspec` command (default:
+ # `"spec"`). Allows you to just type `rspec` instead of `rspec spec` to
+ # run all the examples in the `spec` directory.
+ add_setting :default_path
+
+ # Run examples over DRb (default: `false`). RSpec doesn't supply the DRb
+ # server, but you can use tools like spork.
add_setting :drb
+
+ # The drb_port (default: `8989`).
add_setting :drb_port
- add_setting :profile_examples
+
+ # Default: `$stderr`.
+ add_setting :error_stream
+
+ # Clean up and exit after the first failure (default: `false`).
add_setting :fail_fast
+
+ # The exit code to return if there are any failures (default: 1).
add_setting :failure_exit_code
- add_setting :run_all_when_everything_filtered
+
+ # Determines the order in which examples are run (default: OS standard
+ # load order for files, declaration order for groups and examples).
+ define_reader :order
+
+ # Default: `$stdout`.
+ # Also known as `output` and `out`
+ add_setting :output_stream, :alias_with => [:output, :out]
+
+ # Load files matching this pattern (default: `'**/*_spec.rb'`)
add_setting :pattern, :alias_with => :filename_pattern
- add_setting :files_to_run
- add_setting :include_or_extend_modules
- add_setting :backtrace_clean_patterns
- add_setting :tty
+
+ # Report the times for the 10 slowest examples (default: `false`).
+ add_setting :profile_examples
+
+ # Run all examples if none match the configured filters (default: `false`).
+ add_setting :run_all_when_everything_filtered
+
+ # Seed for random ordering (default: generated randomly each run).
+ #
+ # When you run specs with `--order random`, RSpec generates a random seed
+ # for the randomization and prints it to the `output_stream` (assuming
+ # you're using RSpec's built-in formatters). If you discover an ordering
+ # dependency (i.e. examples fail intermittently depending on order), set
+ # this (on Configuration or on the command line with `--seed`) to run
+ # using the same seed while you debug the issue.
+ #
+ # We recommend, actually, that you use the command line approach so you
+ # don't accidentally leave the seed encoded.
+ define_reader :seed
+
+ # When a block passed to pending fails (as expected), display the failure
+ # without reporting it as a failure (default: false).
+ add_setting :show_failures_in_pending_blocks
+
+ # Convert symbols to hashes with the symbol as a key with a value of
+ # `true` (default: false).
+ #
+ # This allows you to tag a group or example like this:
+ #
+ # describe "something slow", :slow do
+ # # ...
+ # end
+ #
+ # ... instead of having to type:
+ #
+ # describe "something slow", :slow => true do
+ # # ...
+ # end
add_setting :treat_symbols_as_metadata_keys_with_true_values
+
+ # @private
+ add_setting :tty
+ # @private
+ add_setting :include_or_extend_modules
+ # @private
+ add_setting :files_to_run
+ # @private
add_setting :expecting_with_rspec
- add_setting :default_path
- add_setting :show_failures_in_pending_blocks
- add_setting :order
- add_setting :seed
+ # @private
+ attr_accessor :filter_manager
DEFAULT_BACKTRACE_PATTERNS = [
/\/lib\d*\/ruby\//,
@@ -117,44 +193,39 @@ def initialize
@seed = srand % 0xFFFF
end
- attr_accessor :filter_manager
-
- # @api private
+ # @private
#
# Used to set higher priority option values from the command line.
def force(hash)
- # TODO - remove the duplication between this and seed=
if hash.has_key?(:seed)
- hash[:seed] = hash[:seed].to_i
- hash[:order] = "rand"
- self.seed = hash[:seed]
- end
-
- # TODO - remove the duplication between this and order=
- if hash.has_key?(:order)
- self.order = hash[:order]
- order, seed = hash[:order].split(":")
- hash[:order] = order
- hash[:seed] = seed.to_i if seed
+ hash[:order], hash[:seed] = order_and_seed_from_seed(hash[:seed])
+ elsif hash.has_key?(:order)
+ set_order_and_seed(hash)
end
@preferred_options.merge!(hash)
end
- def force_include(hash)
- filter_manager.include hash
- end
-
- def force_exclude(hash)
- filter_manager.exclude hash
- end
-
+ # @private
def reset
@reporter = nil
@formatters.clear
end
# @overload add_setting(name)
- # @overload add_setting(name, options_hash)
+ # @overload add_setting(name, opts)
+ # @option opts [Symbol] :default
+ #
+ # set a default value for the generated getter and predicate methods:
+ #
+ # add_setting(:foo, :default => "default value")
+ #
+ # @option opts [Symbol] :alias_with
+ #
+ # Use `:alias_with` to alias the setter, getter, and predicate to another
+ # name, or names:
+ #
+ # add_setting(:foo, :alias_with => :bar)
+ # add_setting(:foo, :alias_with => [:bar, :baz])
#
# Adds a custom setting to the RSpec.configuration object.
#
@@ -175,23 +246,6 @@ def reset
# RSpec.configuration.foo=(value)
# RSpec.configuration.foo
# RSpec.configuration.foo? # returns true if foo returns anything but nil or false
- #
- # ### Options
- #
- # `add_setting` takes an optional hash that supports the keys `:default`
- # and `:alias_with`.
- #
- # Use `:default` to set a default value for the generated getter and
- # predicate methods:
- #
- # add_setting(:foo, :default => "default value")
- #
- # Use `:alias_with` to alias the setter, getter, and predicate to another
- # name, or names:
- #
- # add_setting(:foo, :alias_with => :bar)
- # add_setting(:foo, :alias_with => [:bar, :baz])
- #
def add_setting(name, opts={})
default = opts.delete(:default)
(class << self; self; end).class_eval do
@@ -203,6 +257,9 @@ def add_setting(name, opts={})
# Used by formatters to ask whether a backtrace line should be displayed
# or not, based on the line matching any `backtrace_clean_patterns`.
def cleaned_from_backtrace?(line)
+ # TODO (David 2011-12-25) why are we asking the configuration to do
+ # stuff? Either use the patterns directly or enapsulate the filtering
+ # in a BacktraceCleaner object.
backtrace_clean_patterns.any? { |regex| line =~ regex }
end
@@ -408,41 +465,13 @@ def reporter
end
end
+ # @private
def files_or_directories_to_run=(*files)
files = files.flatten
files << default_path if command == 'rspec' && default_path && files.empty?
self.files_to_run = get_files_to_run(files)
end
- # @api private
- def command
- $0.split(File::SEPARATOR).last
- end
-
- # @api private
- def get_files_to_run(paths)
- patterns = pattern.split(",")
- paths.map do |path|
- File.directory?(path) ? gather_directories(path, patterns) : extract_location(path)
- end.flatten
- end
-
- # @api private
- def gather_directories(path, patterns)
- patterns.map do |pattern|
- pattern =~ /^#{path}/ ? Dir[pattern.strip] : Dir["#{path}/{#{pattern.strip}}"]
- end
- end
-
- # @api private
- def extract_location(path)
- if path =~ /^(.*?)((?:\:\d+)+)$/
- path, lines = $1, $2[1..-1].split(":").map{|n| n.to_i}
- filter_manager.add_location path, lines
- end
- path
- end
-
# Creates a method that delegates to `example` including the submitted
# `args`. Used internally to add variants of `example` like `pending`:
#
@@ -510,7 +539,7 @@ def alias_it_should_behave_like_to(new_name, report_label = '')
# # with treat_symbols_as_metadata_keys_with_true_values = true
# filter_run_including :foo # results in {:foo => true}
def filter_run_including(*args)
- filter_manager.include :low_priority, build_metadata_hash_from(args)
+ filter_manager.include_with_low_priority build_metadata_hash_from(args)
end
alias_method :filter_run, :filter_run_including
@@ -523,7 +552,7 @@ def filter_run_including(*args)
# This overrides any inclusion filters/tags set on the command line or in
# configuration files.
def inclusion_filter=(filter)
- filter_manager.include :replace, build_metadata_hash_from([filter])
+ filter_manager.include! build_metadata_hash_from([filter])
end
alias_method :filter=, :inclusion_filter=
@@ -552,7 +581,7 @@ def inclusion_filter
# # with treat_symbols_as_metadata_keys_with_true_values = true
# filter_run_excluding :foo # results in {:foo => true}
def filter_run_excluding(*args)
- filter_manager.exclude :low_priority, build_metadata_hash_from(args)
+ filter_manager.exclude_with_low_priority build_metadata_hash_from(args)
end
# Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't
@@ -563,7 +592,7 @@ def filter_run_excluding(*args)
# This overrides any exclusion filters/tags set on the command line or in
# configuration files.
def exclusion_filter=(filter)
- filter_manager.exclude :replace, build_metadata_hash_from([filter])
+ filter_manager.exclude! build_metadata_hash_from([filter])
end
# Returns the `exclusion_filter`. If none has been set, returns an empty
@@ -572,17 +601,76 @@ def exclusion_filter
filter_manager.exclusions
end
- def include(mod, *args)
- filters = build_metadata_hash_from(args)
- include_or_extend_modules << [:include, mod, filters]
+ # Tells RSpec to include `mod` in example groups. Methods defined in
+ # `mod` are exposed to examples (not example groups). Use `filters` to
+ # constrain the groups in which to include the module.
+ #
+ # @example
+ #
+ # module AuthenticationHelpers
+ # def login_as(user)
+ # # ...
+ # end
+ # end
+ #
+ # module UserHelpers
+ # def users(username)
+ # # ...
+ # end
+ # end
+ #
+ # RSpec.configure do |config|
+ # config.include(UserHelpers) # included in all modules
+ # config.include(AuthenticationHelpers, :type => :request)
+ # end
+ #
+ # describe "edit profile", :type => :request do
+ # it "can be viewed by owning user" do
+ # login_as users(:jdoe)
+ # get "/profiles/jdoe"
+ # assert_select ".username", :text => 'jdoe'
+ # end
+ # end
+ #
+ # @see #extend
+ def include(mod, *filters)
+ include_or_extend_modules << [:include, mod, build_metadata_hash_from(filters)]
end
- def extend(mod, *args)
- filters = build_metadata_hash_from(args)
- include_or_extend_modules << [:extend, mod, filters]
+ # Tells RSpec to extend example groups with `mod`. Methods defined in
+ # `mod` are exposed to example groups (not examples). Use `filters` to
+ # constrain the groups to extend.
+ #
+ # Similar to `include`, but behavior is added to example groups, which
+ # are classes, rather than the examples, which are instances of those
+ # classes.
+ #
+ # @example
+ #
+ # module UiHelpers
+ # def run_in_browser
+ # # ...
+ # end
+ # end
+ #
+ # RSpec.configure do |config|
+ # config.extend(UiHelpers, :type => :request)
+ # end
+ #
+ # describe "edit profile", :type => :request do
+ # run_in_browser
+ #
+ # it "does stuff in the client" do
+ # # ...
+ # end
+ # end
+ #
+ # @see #include
+ def extend(mod, *filters)
+ include_or_extend_modules << [:extend, mod, build_metadata_hash_from(filters)]
end
- # @api private
+ # @private
#
# Used internally to extend a group with modules using `include` and/or
# `extend`.
@@ -593,46 +681,36 @@ def configure_group(group)
end
end
+ # @private
def configure_mock_framework
RSpec::Core::ExampleGroup.send(:include, mock_framework)
end
+ # @private
def configure_expectation_framework
expectation_frameworks.each do |framework|
RSpec::Core::ExampleGroup.send(:include, framework)
end
end
+ # @private
def load_spec_files
- files_to_run.map {|f| load File.expand_path(f) }
+ files_to_run.uniq.map {|f| load File.expand_path(f) }
raise_if_rspec_1_is_loaded
end
- remove_method :seed=
# @api
#
# Sets the seed value and sets `order='rand'`
def seed=(seed)
- # TODO - remove the duplication between this and force
- @order = 'rand'
- @seed = seed.to_i
+ order_and_seed_from_seed(seed)
end
- remove_method :order=
-
# @api
#
# Sets the order and, if order is `'rand:<seed>'`, also sets the seed.
def order=(type)
- # TODO - remove the duplication between this and force
- order, seed = type.to_s.split(':')
- if order == 'default'
- @order = nil
- @seed = nil
- else
- @order = order
- @seed = seed.to_i if seed
- end
+ order_and_seed_from_order(type)
end
def randomize?
@@ -641,6 +719,31 @@ def randomize?
private
+ def get_files_to_run(paths)
+ patterns = pattern.split(",")
+ paths.map do |path|
+ File.directory?(path) ? gather_directories(path, patterns) : extract_location(path)
+ end.flatten
+ end
+
+ def gather_directories(path, patterns)
+ patterns.map do |pattern|
+ pattern =~ /^#{path}/ ? Dir[pattern.strip] : Dir["#{path}/{#{pattern.strip}}"]
+ end
+ end
+
+ def extract_location(path)
+ if path =~ /^(.*?)((?:\:\d+)+)$/
+ path, lines = $1, $2[1..-1].split(":").map{|n| n.to_i}
+ filter_manager.add_location path, lines
+ end
+ path
+ end
+
+ def command
+ $0.split(File::SEPARATOR).last
+ end
+
def value_for(key, default=nil)
@preferred_options.has_key?(key) ? @preferred_options[key] : default
end
@@ -736,6 +839,23 @@ def file_at(path)
File.new(path, 'w')
end
+ def order_and_seed_from_seed(value)
+ @order, @seed = 'rand', value.to_i
+ end
+
+ def set_order_and_seed(hash)
+ hash[:order], seed = order_and_seed_from_order(hash[:order])
+ hash[:seed] = seed if seed
+ end
+
+ def order_and_seed_from_order(type)
+ order, seed = type.to_s.split(':')
+ @order = order
+ @seed = seed = seed.to_i if seed
+ @order, @seed = nil, nil if order == 'default'
+ return order, seed
+ end
+
end
end
end
View
6 lib/rspec/core/configuration_options.rb
@@ -2,6 +2,7 @@
module RSpec
module Core
+ # @private
class ConfigurationOptions
attr_reader :options
@@ -37,7 +38,10 @@ def filter_manager
private
- NON_FORCED_OPTIONS = [:debug, :requires, :libs, :files_or_directories_to_run, :line_numbers, :full_description]
+ NON_FORCED_OPTIONS = [
+ :debug, :requires, :libs, :profile, :drb, :files_or_directories_to_run,
+ :line_numbers, :full_description, :full_backtrace, :tty
+ ].to_set
def force?(key)
!NON_FORCED_OPTIONS.include?(key)
View
5 lib/rspec/core/deprecation.rb
@@ -1,7 +1,6 @@
module RSpec
-
class << self
- # @api private
+ # @private
#
# Used internally to print deprecation warnings
def deprecate(method, alternate_method=nil, version=nil)
@@ -27,7 +26,7 @@ def deprecate(method, alternate_method=nil, version=nil)
warn_deprecation(message)
end
- # @api private
+ # @private
#
# Used internally to print deprecation warnings
def warn_deprecation(message)
View
129 lib/rspec/core/drb_options.rb
@@ -1,72 +1,85 @@
# Builds command line arguments to pass to the rspec command over DRb
-class RSpec::Core::DrbOptions
- def initialize(submitted_options, filter_manager)
- @submitted_options = submitted_options
- @filter_manager = filter_manager
- end
- def options
- argv = []
- argv << "--color" if @submitted_options[:color]
- argv << "--profile" if @submitted_options[:profile_examples]
- argv << "--backtrace" if @submitted_options[:full_backtrace]
- argv << "--tty" if @submitted_options[:tty]
- argv << "--fail-fast" if @submitted_options[:fail_fast]
- argv << "--options" << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file]
- argv << "--order" << @submitted_options[:order] if @submitted_options[:order]
+module RSpec::Core
+ # @private
+ class DrbOptions
+ def initialize(submitted_options, filter_manager)
+ @submitted_options = submitted_options
+ @filter_manager = filter_manager
+ end
- add_full_description(argv)
- add_line_numbers(argv)
- add_filter(argv, :inclusion, @filter_manager.inclusions)
- add_filter(argv, :exclusion, @filter_manager.exclusions)
- add_formatters(argv)
- add_libs(argv)
- add_requires(argv)
+ def options
+ argv = []
+ argv << "--color" if @submitted_options[:color]
+ argv << "--profile" if @submitted_options[:profile_examples]
+ argv << "--backtrace" if @submitted_options[:full_backtrace]
+ argv << "--tty" if @submitted_options[:tty]
+ argv << "--fail-fast" if @submitted_options[:fail_fast]
+ argv << "--options" << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file]
+ argv << "--order" << @submitted_options[:order] if @submitted_options[:order]
- argv + @submitted_options[:files_or_directories_to_run]
- end
+ add_failure_exit_code(argv)
+ add_full_description(argv)
+ add_line_numbers(argv)
+ add_filter(argv, :inclusion, @filter_manager.inclusions)
+ add_filter(argv, :exclusion, @filter_manager.exclusions)
+ add_formatters(argv)
+ add_libs(argv)
+ add_requires(argv)
- def add_full_description(argv)
- if @submitted_options[:full_description]
- # The argument to --example is regexp-escaped before being stuffed
- # into a regexp when received for the first time (see OptionParser).
- # Hence, merely grabbing the source of this regexp will retain the
- # backslashes, so we must remove them.
- argv << "--example" << @submitted_options[:full_description].source.delete('\\')
+ argv + @submitted_options[:files_or_directories_to_run]
end
- end
- def add_line_numbers(argv)
- if @submitted_options[:line_numbers]
- argv.push(*@submitted_options[:line_numbers].inject([]){|a,l| a << "--line_number" << l})
+ def add_failure_exit_code(argv)
+ if @submitted_options[:failure_exit_code]
+ argv << "--failure-exit-code" << @submitted_options[:failure_exit_code].to_s
+ end
end
- end
- def add_filter(argv, name, hash)
- hash.each_pair do |k, v|
- next if [:if,:unless].include?(k)
- tag = name == :inclusion ? k.to_s : "~#{k.to_s}"
- tag << ":#{v.to_s}" if v.is_a?(String)
- argv << "--tag" << tag
- end unless hash.empty?
- end
+ def add_full_description(argv)
+ if @submitted_options[:full_description]
+ # The argument to --example is regexp-escaped before being stuffed
+ # into a regexp when received for the first time (see OptionParser).
+ # Hence, merely grabbing the source of this regexp will retain the
+ # backslashes, so we must remove them.
+ argv << "--example" << @submitted_options[:full_description].source.delete('\\')
+ end
+ end
- def add_formatters(argv)
- @submitted_options[:formatters].each do |pair|
- argv << "--format" << pair[0]
- argv << "--out" << pair[1] if pair[1]
- end if @submitted_options[:formatters]
- end
+ def add_line_numbers(argv)
+ if @submitted_options[:line_numbers]
+ argv.push(*@submitted_options[:line_numbers].inject([]){|a,l| a << "--line_number" << l})
+ end
+ end
- def add_libs(argv)
- @submitted_options[:libs].each do |path|
- argv << "-I" << path
- end if @submitted_options[:libs]
- end
+ CONDITIONAL_FILTERS = [:if, :unless].to_set
+
+ def add_filter(argv, name, hash)
+ hash.each_pair do |k, v|
+ next if CONDITIONAL_FILTERS.include?(k)
+ tag = name == :inclusion ? k.to_s : "~#{k}"
+ tag << ":#{v}" if v.is_a?(String)
+ argv << "--tag" << tag
+ end unless hash.empty?
+ end
+
+ def add_formatters(argv)
+ @submitted_options[:formatters].each do |pair|
+ argv << "--format" << pair[0]
+ argv << "--out" << pair[1] if pair[1]
+ end if @submitted_options[:formatters]
+ end
+
+ def add_libs(argv)
+ @submitted_options[:libs].each do |path|
+ argv << "-I" << path
+ end if @submitted_options[:libs]
+ end
- def add_requires(argv)
- @submitted_options[:requires].each do |path|
- argv << "--require" << path
- end if @submitted_options[:requires]
+ def add_requires(argv)
+ @submitted_options[:requires].each do |path|
+ argv << "--require" << path
+ end if @submitted_options[:requires]
+ end
end
end
View
13 lib/rspec/core/dsl.rb
@@ -1,6 +1,19 @@
module RSpec
module Core
+ # Adds the `describe` method to the top-level namespace.
module DSL
+ # Generates a subclass of [ExampleGroup](ExampleGroup)
+ #
+ # ## Examples:
+ #
+ # describe "something" do
+ # it "does something" do
+ # # example code goes here
+ # end
+ # end
+ #
+ # @see ExampleGroup
+ # @see ExampleGroup.describe
def describe(*args, &example_group_block)
RSpec::Core::ExampleGroup.describe(*args, &example_group_block).register
end
View
13 lib/rspec/core/errors.rb
@@ -1,13 +0,0 @@
-module RSpec
- module Core
- # If Test::Unit is loaed, we'll use its error as baseclass, so that Test::Unit
- # will report unmet RSpec expectations as failures rather than errors.
- begin
- class PendingExampleFixedError < Test::Unit::AssertionFailedError; end
- rescue
- class PendingExampleFixedError < StandardError; end
- end
- end
-end
-
-
View
40 lib/rspec/core/example.rb
@@ -8,7 +8,7 @@ module Core
# [around](Hooks#around-instance_method) hooks.
# @see ExampleGroup
class Example
- # @api private
+ # @private
#
# Used to define methods that delegate to this example's metadata
def self.delegate_to_metadata(*keys)
@@ -31,7 +31,7 @@ def self.delegate_to_metadata(*keys)
attr_reader :metadata
# @attr_reader
- # @api private
+ # @private
#
# Returns the example_group_instance that provides the context for
# running this example.
@@ -62,7 +62,7 @@ def example_group
alias_method :pending?, :pending
- # @api
+ # @api private
# @param example_group_instance the instance of an ExampleGroup subclass
# instance_evals the block submitted to the constructor in the
# context of the instance of ExampleGroup
@@ -105,19 +105,19 @@ def run(example_group_instance, reporter)
finish(reporter)
end
- # @api private
+ # @private
#
# Wraps the example block in a Proc so it can invoked using `run` or
# `call` in [around](../Hooks#around-instance_method) hooks.
def self.procsy(metadata, &proc)
Proc.new(&proc).extend(Procsy).with(metadata)
end
- # @api private
+ # @private
module Procsy
attr_reader :metadata
- # @api private
+ # @private
# @param [Proc]
# Adds a `run` method to the extended Proc, allowing it to be invoked
# in an [around](../Hooks#around-instance_method) hook using either
@@ -126,32 +126,37 @@ def self.extended(object)
def object.run; call; end
end
- # @api private
+ # @private
def with(metadata)
@metadata = metadata
self
end
end
- # @api private
+ # @private
+ def any_apply?(filters)
+ metadata.any_apply?(filters)
+ end
+
+ # @private
def all_apply?(filters)
@metadata.all_apply?(filters) || @example_group_class.all_apply?(filters)
end
- # @api private
+ # @private
def around_hooks
@around_hooks ||= example_group.around_hooks_for(self)
end
- # @api private
+ # @private
#
# Used internally to set an exception in an after hook, which
# captures the exception but doesn't raise it.
def set_exception(exception)
@exception ||= exception
end
- # @api private
+ # @private
#
# Used internally to set an exception and fail without actually executing
# the example when an exception is raised in before(:all).
@@ -161,11 +166,6 @@ def fail_with_exception(reporter, exception)
finish(reporter)
end
- # @api private
- def any_apply?(filters)
- metadata.any_apply?(filters)
- end
-
private
def with_around_hooks(&block)
@@ -181,8 +181,14 @@ def start(reporter)
record :started_at => Time.now
end
+ # @private
+ module NotPendingExampleFixed
+ def pending_fixed?; false; end
+ end
+
def finish(reporter)
if @exception
+ @exception.extend(NotPendingExampleFixed) unless @exception.respond_to?(:pending_fixed?)
record_finished 'failed', :exception => @exception
reporter.example_failed self
false
@@ -191,7 +197,7 @@ def finish(reporter)
reporter.example_pending self
true
elsif pending
- record_finished 'pending', :pending_message => 'Not Yet Implemented'
+ record_finished 'pending', :pending_message => String === pending ? pending : Pending::NO_REASON_GIVEN
reporter.example_pending self
true
else
View
109 lib/rspec/core/example_group.rb
@@ -23,15 +23,18 @@ class ExampleGroup
include Pending
include Let
+ # @private
def self.world
RSpec.world
end
+ # @private
def self.register
world.register(self)
end
class << self
+ # @private
def self.delegate_to_metadata(*names)
names.each do |name|
define_method name do
@@ -42,14 +45,16 @@ def self.delegate_to_metadata(*names)
delegate_to_metadata :description, :described_class, :file_path
alias_method :display_name, :description
+ # @private
alias_method :describes, :described_class
end
+ # @private
def self.define_example_method(name, extra_options={})
module_eval(<<-END_RUBY, __FILE__, __LINE__)
def self.#{name}(desc=nil, *args, &block)
options = build_metadata_hash_from(args)
- options.update(:pending => true) unless block
+ options.update(:pending => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
options.update(#{extra_options.inspect})
examples << RSpec::Core::Example.new(self, desc, options, block)
examples.last
@@ -58,22 +63,22 @@ def self.#{name}(desc=nil, *args, &block)
end
define_example_method :example
+ define_example_method :it
+ define_example_method :specify
+
+ define_example_method :focused, :focused => true, :focus => true
+ define_example_method :focus, :focused => true, :focus => true
+
+ define_example_method :pending, :pending => true
+ define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
+ define_example_method :xit, :pending => 'Temporarily disabled with xit'
+ define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
class << self
alias_method :alias_example_to, :define_example_method
end
- alias_example_to :it
- alias_example_to :specify
-
- alias_example_to :pending, :pending => true
- alias_example_to :xexample, :pending => true
- alias_example_to :xit, :pending => true
- alias_example_to :xspecify, :pending => true
-
- alias_example_to :focused, :focused => true, :focus => true
- alias_example_to :focus, :focused => true, :focus => true
-
+ # @private
def self.define_nested_shared_group_method(new_name, report_label=nil)
module_eval(<<-END_RUBY, __FILE__, __LINE__)
def self.#{new_name}(name, *args, &customization_block)
@@ -108,10 +113,12 @@ def self.include_examples(name, *args)
block_given? ? block_not_supported("examples") : find_and_eval_shared("examples", name, *args)
end
+ # @private
def self.block_not_supported(label)
warn("Customization blocks not supported for include_#{label}. Use it_behaves_like instead.")
end
+ # @private
def self.find_and_eval_shared(label, name, *args, &customization_block)
raise ArgumentError, "Could not find shared #{label} #{name.inspect}" unless
shared_block = world.shared_example_groups[name]
@@ -120,29 +127,54 @@ def self.find_and_eval_shared(label, name, *args, &customization_block)
module_eval(&customization_block) if customization_block
end
+ # @private
def self.examples
@examples ||= []
end
+ # @private
def self.filtered_examples
world.filtered_examples[self]
end
+ # @private
def self.descendant_filtered_examples
@descendant_filtered_examples ||= filtered_examples + children.inject([]){|l,c| l + c.descendant_filtered_examples}
end
+ # The [Metadata](Metadata) object associated with this group.
# @see Metadata
def self.metadata
@metadata if defined?(@metadata)
end
- # @api private
+ # @private
# @return [Metadata] belonging to the parent of a nested [ExampleGroup](ExampleGroup)
def self.superclass_metadata
@superclass_metadata ||= self.superclass.respond_to?(:metadata) ? self.superclass.metadata : nil
end
+ # Generates a subclass of this example group which inherits
+ # everything except the examples themselves.
+ #
+ # ## Examples
+ #
+ # describe "something" do # << This describe method is defined in
+ # # << RSpec::Core::DSL, included in the
+ # # << global namespace
+ # before do
+ # do_something_before
+ # end
+ #
+ # let(:thing) { Thing.new }
+ #
+ # describe "attribute (of something)" do
+ # # examples in the group get the before hook
+ # # declared above, and can access `thing`
+ # end
+ # end
+ #
+ # @see DSL#describe
def self.describe(*args, &example_group_block)
@_subclass_count ||= 0
@_subclass_count += 1
@@ -162,29 +194,35 @@ class << self
alias_method :context, :describe
end
+ # @private
def self.subclass(parent, args, &example_group_block)
subclass = Class.new(parent)
subclass.set_it_up(*args)
subclass.module_eval(&example_group_block) if example_group_block
subclass
end
+ # @private
def self.children
@children ||= [].extend(Extensions::Ordered)
end
+ # @private
def self.descendants
@_descendants ||= [self] + children.inject([]) {|list, c| list + c.descendants}
end
+ # @private
def self.ancestors
@_ancestors ||= super().select {|a| a < RSpec::Core::ExampleGroup}
end
+ # @private
def self.top_level?
@top_level ||= superclass == ExampleGroup
end
+ # @private
def self.ensure_example_groups_are_configured
unless defined?(@@example_groups_configured)
RSpec.configuration.configure_mock_framework
@@ -193,6 +231,7 @@ def self.ensure_example_groups_are_configured
end
end
+ # @private
def self.set_it_up(*args)
# Ruby 1.9 has a bug that can lead to infinite recursion and a
# SystemStackError if you include a module in a superclass after
@@ -210,22 +249,26 @@ def self.set_it_up(*args)
world.configure_group(self)
end
+ # @private
def self.before_all_ivars
@before_all_ivars ||= {}
end
+ # @private
def self.store_before_all_ivars(example_group_instance)
return if example_group_instance.instance_variables.empty?
example_group_instance.instance_variables.each { |ivar|
before_all_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
}
end
+ # @private
def self.assign_before_all_ivars(ivars, example_group_instance)
return if ivars.empty?
ivars.each { |ivar, val| example_group_instance.instance_variable_set(ivar, val) }
end
+ # @private
def self.run_before_all_hooks(example_group_instance)
return if descendant_filtered_examples.empty?
assign_before_all_ivars(superclass.before_all_ivars, example_group_instance)
@@ -234,6 +277,7 @@ def self.run_before_all_hooks(example_group_instance)
store_before_all_ivars(example_group_instance)
end
+ # @private
def self.run_around_each_hooks(example, initial_procsy)
example.around_hooks.reverse.inject(initial_procsy) do |procsy, around_hook|
Example.procsy(procsy.metadata) do
@@ -242,16 +286,19 @@ def self.run_around_each_hooks(example, initial_procsy)
end
end
+ # @private
def self.run_before_each_hooks(example)
world.run_hook_filtered(:before, :each, self, example.example_group_instance, example)
ancestors.reverse.each { |ancestor| ancestor.run_hook(:before, :each, example.example_group_instance) }
end
+ # @private
def self.run_after_each_hooks(example)
ancestors.each { |ancestor| ancestor.run_hook(:after, :each, example.example_group_instance) }
world.run_hook_filtered(:after, :each, self, example.example_group_instance, example)
end
+ # @private
def self.run_after_all_hooks(example_group_instance)
return if descendant_filtered_examples.empty?
assign_before_all_ivars(before_all_ivars, example_group_instance)
@@ -272,10 +319,12 @@ def self.run_after_all_hooks(example_group_instance)
world.run_hook_filtered(:after, :all, self, example_group_instance)
end
+ # @private
def self.around_hooks_for(example)
world.find_hook(:around, :each, self, example) + ancestors.reverse.inject([]){|l,a| l + a.find_hook(:around, :each, self, example)}
end
+ # Runs all the examples in this group
def self.run(reporter)
if RSpec.wants_to_quit
RSpec.clear_remaining_example_groups if top_level?
@@ -297,6 +346,19 @@ def self.run(reporter)
end
end
+ # @private
+ def self.run_examples(reporter)
+ filtered_examples.ordered.map do |example|
+ next if RSpec.wants_to_quit
+ instance = new
+ set_ivars(instance, before_all_ivars)
+ succeeded = example.run(instance, reporter)
+ RSpec.wants_to_quit = true if fail_fast? && !succeeded
+ succeeded
+ end.all?
+ end
+
+ # @private
def self.fail_filtered_examples(exception, reporter)
filtered_examples.each { |example| example.fail_with_exception(reporter, exception) }
@@ -308,41 +370,34 @@ def self.fail_filtered_examples(exception, reporter)
false
end
+ # @private
def self.fail_fast?
RSpec.configuration.fail_fast?
end
- def self.run_examples(reporter)
- filtered_examples.ordered.map do |example|
- next if RSpec.wants_to_quit
- instance = new
- set_ivars(instance, before_all_ivars)
- succeeded = example.run(instance, reporter)
- RSpec.wants_to_quit = true if fail_fast? && !succeeded
- succeeded
- end.all?
- end
-
- # @api private
+ # @private
def self.any_apply?(filters)
metadata.any_apply?(filters)
end
- # @api private
+ # @private
def self.all_apply?(filters)
metadata.all_apply?(filters)
end
+ # @private
def self.declaration_line_numbers
@declaration_line_numbers ||= [metadata[:example_group][:line_number]] +
examples.collect {|e| e.metadata[:line_number]} +
children.inject([]) {|l,c| l + c.declaration_line_numbers}
end
+ # @private
def self.top_level_description
ancestors.last.description
end
+ # @private
def self.set_ivars(instance, ivars)
ivars.each {|name, value| instance.instance_variable_set(name, value)}
end