Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #852 from rspec/fix_sandboxing

Fix sandboxing of rspec-core's own specs.
  • Loading branch information...
commit f4cca6fa3c121774d223e0e1ad3fb71e5ba156aa 2 parents faf02d9 + e40d30d
@myronmarston myronmarston authored
View
1  lib/rspec/core/pending.rb
@@ -92,6 +92,7 @@ def pending(*args)
result = begin
yield
example.example_group_instance.instance_eval { verify_mocks_for_rspec }
+ true
end
example.metadata[:pending] = false
rescue Exception => e
View
58 spec/spec_helper.rb
@@ -35,37 +35,37 @@ def method_missing(method, *args, &block)
end
end
- def sandboxed(&block)
- @orig_config = RSpec.configuration
- @orig_world = RSpec.world
- new_config = RSpec::Core::Configuration.new
- new_world = RSpec::Core::World.new(new_config)
- RSpec.instance_variable_set(:@configuration, new_config)
- RSpec.instance_variable_set(:@world, new_world)
- object = Object.new
- object.extend(RSpec::Core::SharedExampleGroup)
-
- (class << RSpec::Core::ExampleGroup; self; end).class_eval do
- alias_method :orig_run, :run
- def run(reporter=nil)
- @orig_mock_space = RSpec::Mocks::space
- RSpec::Mocks::space = RSpec::Mocks::Space.new
- orig_run(reporter || NullObject.new)
- ensure
- RSpec::Mocks::space = @orig_mock_space
+ module Sandboxing
+ def self.sandboxed(&block)
+ @orig_config = RSpec.configuration
+ @orig_world = RSpec.world
+ new_config = RSpec::Core::Configuration.new
+ new_world = RSpec::Core::World.new(new_config)
+ RSpec.instance_variable_set(:@configuration, new_config)
+ RSpec.instance_variable_set(:@world, new_world)
+ object = Object.new
+ object.extend(RSpec::Core::SharedExampleGroup)
+
+ (class << RSpec::Core::ExampleGroup; self; end).class_eval do
+ alias_method :orig_run, :run
+ def run(reporter=nil)
+ orig_run(reporter || NullObject.new)
+ end
end
- end
- object.instance_eval(&block)
- ensure
- (class << RSpec::Core::ExampleGroup; self; end).class_eval do
- remove_method :run
- alias_method :run, :orig_run
- remove_method :orig_run
- end
+ RSpec::Core::SandboxedMockSpace.sandboxed do
+ object.instance_eval(&block)
+ end
+ ensure
+ (class << RSpec::Core::ExampleGroup; self; end).class_eval do
+ remove_method :run
+ alias_method :run, :orig_run
+ remove_method :orig_run
+ end
- RSpec.instance_variable_set(:@configuration, @orig_config)
- RSpec.instance_variable_set(:@world, @orig_world)
+ RSpec.instance_variable_set(:@configuration, @orig_config)
+ RSpec.instance_variable_set(:@world, @orig_world)
+ end
end
def in_editor?
@@ -99,7 +99,7 @@ def without_env_vars(*vars)
RSpec.configure do |c|
# structural
c.alias_it_behaves_like_to 'it_has_behavior'
- c.around {|example| sandboxed { example.run }}
+ c.around {|example| Sandboxing.sandboxed { example.run }}
c.include(RSpecHelpers)
c.include Aruba::Api, :example_group => {
:file_path => /spec\/command_line/
View
100 spec/support/sandboxed_mock_space.rb
@@ -0,0 +1,100 @@
+require 'rspec/mocks'
+
+module RSpec
+ module Core
+ # Because rspec-core dog-foods itself, rspec-core's spec suite has
+ # examples that define example groups and examples and run them. The
+ # usual lifetime of an RSpec::Mocks::Proxy is for one example
+ # (the proxy cache gets cleared between each example), but since the
+ # specs in rspec-core's suite sometimes create test doubles and pass
+ # them to examples a spec defines and runs, the test double's proxy
+ # must live beyond the inner example: it must live for the scope
+ # of wherever it got defined. Here we implement the necessary semantics
+ # for rspec-core's specs:
+ #
+ # - #verify_all and #reset_all affect only mocks that were created
+ # within the current scope.
+ # - Mock proxies live for the duration of the scope in which they are
+ # created.
+ #
+ # Thus, mock proxies created in an inner example live for only that
+ # example, but mock proxies created in an outer example can be used
+ # in an inner example but will only be reset/verified when the outer
+ # example completes.
+ class SandboxedMockSpace < ::RSpec::Mocks::Space
+ def self.sandboxed
+ orig_space = RSpec::Mocks.space
+ RSpec::Mocks.space = RSpec::Core::SandboxedMockSpace.new
+
+ RSpec::Core::Example.class_eval do
+ alias_method :orig_run, :run
+ def run(*args)
+ RSpec::Mocks.space.sandboxed do
+ orig_run(*args)
+ end
+ end
+ end
+
+ yield
+ ensure
+ RSpec::Core::Example.class_eval do
+ remove_method :run
+ alias_method :run, :orig_run
+ remove_method :orig_run
+ end
+
+ RSpec::Mocks.space = orig_space
+ end
+
+ class Sandbox
+ attr_reader :proxies
+
+ def initialize
+ @proxies = Set.new
+ end
+
+ def verify_all
+ @proxies.each { |p| p.verify }
+ end
+
+ def reset_all
+ @proxies.each { |p| p.reset }
+ end
+ end
+
+ def initialize
+ @sandbox_stack = []
+ super
+ end
+
+ def sandboxed
+ @sandbox_stack << Sandbox.new
+ yield
+ ensure
+ @sandbox_stack.pop
+ end
+
+ def verify_all
+ return super unless sandbox = @sandbox_stack.last
+ sandbox.verify_all
+ end
+
+ def reset_all
+ return super unless sandbox = @sandbox_stack.last
+ sandbox.reset_all
+ end
+
+ def proxy_for(object)
+ new_proxy = !proxies.has_key?(object.__id__)
+ proxy = super
+
+ if new_proxy && sandbox = @sandbox_stack.last
+ sandbox.proxies << proxy
+ end
+
+ proxy
+ end
+ end
+ end
+end
+
Please sign in to comment.
Something went wrong with that request. Please try again.