rspec-isolation 0.1.0

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in rspec-isolation.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem "ruby-debug19", :require => "ruby-debug"
8
+ end
data/README.mkd ADDED
@@ -0,0 +1,88 @@
1
+ Running Your Test Cases in Isolation
2
+ ====================================
3
+
4
+ The RSpec examples are generally run in a single ruby process. It dosen't matter until your code introduces some **global variables**, which would **be polluted between examples**. A common solution is to reset them in the `after` hook, but it broken the DRY rule and would bring a lots of extra works. The best way is to **run them in separated sub processes**. This gem would help you to achieve this within a single method call.
5
+
6
+ Dependency
7
+ ----------
8
+ This gem is depended on Rspec 2.0 and above. You should have it installed before using this gem.
9
+
10
+ Installation
11
+ ------------
12
+
13
+ sudo gem install rspec-isolation
14
+
15
+ **Note:** The installation would NOT automatically install the rspec 2.0.
16
+
17
+ Usage
18
+ ----------
19
+ Image a spec file exists:
20
+
21
+ $greeting = "Hello, world!"
22
+
23
+ describe "Test with global vars" do
24
+ it "should be replace with my name" do
25
+ $greeting.gsub!(/\bworld\b/, "Kevin")
26
+ $greeting.should == "Hello, Kevin!"
27
+ end
28
+
29
+ it "should be replace with my hometown name" do
30
+ $greeting.gsub!(/\bworld\b/, "Wuhan")
31
+ $greeting.should == "Hello, Wuhan!"
32
+ end
33
+ end
34
+
35
+ The `$greeting` is polluted in the first example and the second example would failed.
36
+
37
+ Now we require this gem:
38
+
39
+ require 'rspec/isolation'
40
+
41
+ Then you can choose one of the following ways to enable the isolation:
42
+
43
+ 1. Using `iso_it`:
44
+
45
+ require 'rspec/isolation'
46
+ $greeting = "Hello, world!"
47
+
48
+ describe "Test with global vars" do
49
+ iso_it "should be replace with my name" do
50
+ $greeting.gsub!(/\bworld\b/, "Kevin")
51
+ $greeting.should == "Hello, Kevin!"
52
+ end
53
+
54
+ it "should be replace with my hometown name" do
55
+ $greeting.gsub!(/\bworld\b/, "Wuhan")
56
+ $greeting.should == "Hello, Wuhan!"
57
+ end
58
+ end
59
+
60
+ This would run the first example in a sub process.
61
+
62
+ 2. Using `run_in_isolation` in `before(:each)` hook:
63
+
64
+ require 'rspec/isolation'
65
+ $greeting = "Hello, world!"
66
+
67
+ describe "Test with global vars" do
68
+ before(:each) do
69
+ run_in_isolation
70
+ end
71
+ it "should be replace with my name" do
72
+ $greeting.gsub!(/\bworld\b/, "Kevin")
73
+ $greeting.should == "Hello, Kevin!"
74
+ end
75
+
76
+ it "should be replace with my hometown name" do
77
+ $greeting.gsub!(/\bworld\b/, "Wuhan")
78
+ $greeting.should == "Hello, Wuhan!"
79
+ end
80
+ end
81
+
82
+ This would run all examples in separated sub process. Note: It does not work
83
+ if you put `run_in_isolation` in a `before(:all)` hook.
84
+
85
+ Author
86
+ ------
87
+ Kevin Fu, corntrace@gmail.com, @corntrace. If you like and use this gem, please feel free to give me any
88
+ recommandation.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,10 @@
1
+ module RSpec
2
+ module Isolation
3
+ def run_in_isolation
4
+ example.metadata[:run_in_isolation] = true
5
+ end
6
+ end
7
+ end
8
+
9
+ # $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
10
+ require 'rspec/isolation/core_ext'
@@ -0,0 +1,71 @@
1
+ RSpec::Core::ExampleGroup.send :include, RSpec::Isolation
2
+ RSpec::Core::ExampleGroup.alias_example_to :run_in_isolation, :run_in_isolation => true
3
+ RSpec::Core::ExampleGroup.alias_example_to :iso_it, :run_in_isolation => true
4
+
5
+ RSpec::Core::Example.class_eval do
6
+
7
+ delegate_to_metadata :run_in_isolation
8
+
9
+ def isolated?
10
+ !!run_in_isolation
11
+ end
12
+
13
+ def isolated_or_normal
14
+ if isolated?
15
+ read, write = IO.pipe
16
+ pid = fork do
17
+ read.close
18
+ begin
19
+ rest = yield
20
+ write.puts [Marshal.dump(rest)].pack("m")
21
+ rescue Exception => e
22
+ write.puts [Marshal.dump(e)].pack("m")
23
+ end
24
+ exit!
25
+ end
26
+ write.close
27
+ result = Marshal.load(read.read.unpack("m").first)
28
+ Process.wait2(pid)
29
+ return result
30
+ else
31
+ yield
32
+ end
33
+ end
34
+
35
+ # Redefine the run method
36
+ def run(example_group_instance, reporter)
37
+ return if RSpec.wants_to_quit
38
+ @example_group_instance = example_group_instance
39
+ @example_group_instance.example = self
40
+
41
+ start(reporter)
42
+
43
+ begin
44
+ unless pending
45
+ with_pending_capture do
46
+ with_around_hooks do
47
+ begin
48
+ run_before_each
49
+ @in_block = true
50
+ result = isolated_or_normal { @example_group_instance.instance_eval(&@example_block) }
51
+ raise result if result.class < Exception
52
+ result
53
+ rescue Exception => e
54
+ set_exception(e)
55
+ ensure
56
+ @in_block = false
57
+ run_after_each
58
+ end
59
+ end
60
+ end
61
+ end
62
+ rescue Exception => e
63
+ set_exception(e)
64
+ ensure
65
+ @example_group_instance.example = nil
66
+ assign_auto_description
67
+ end
68
+
69
+ finish(reporter)
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ module RSpec
2
+ module Isolation
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
3
+ require 'rspec/isolation/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rspec-isolation"
7
+ s.version = RSpec::Isolation::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Kevin Fu"]
10
+ s.email = ["corntrace@gmail.com"]
11
+ s.homepage = "http://rubygems.org/gems/rspec-isolation"
12
+ s.summary = "Make rspec examples executed in separated processes."
13
+ s.description = "Make rspec examples executed in separated processes. \
14
+ Especially used in framework development."
15
+
16
+ s.required_rubygems_version = ">= 1.3.6"
17
+
18
+ s.add_development_dependency "bundler", ">= 1.0.0.rc.5"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.executables = `git ls-files`.split("\n").select{|f| f =~ /^bin/}
22
+ s.require_path = 'lib'
23
+
24
+ s.add_dependency("rspec", ["~> 2.0.0"])
25
+ end
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "#isolation" do
4
+ context "in before each" do
5
+ it "sets the example isolated" do
6
+ group = RSpec::Core::ExampleGroup.describe do
7
+ before(:each) {run_in_isolation}
8
+ example('example') {}
9
+ end
10
+ group.run
11
+ group.examples.first.run_in_isolation.should be_true
12
+ end
13
+ end
14
+ context "in the example" do
15
+ it "using iso_it sets the example isolated" do
16
+ group = RSpec::Core::ExampleGroup.describe do
17
+ iso_it {}
18
+ end
19
+ # group.run
20
+ group.examples.first.run_in_isolation.should be_true
21
+ end
22
+ end
23
+ context "failure and exception capture" do
24
+ it "should capture failures" do
25
+ group = RSpec::Core::ExampleGroup.describe
26
+ example = group.iso_it('example') { 1.should == 2 }
27
+
28
+ group.run
29
+ example.metadata[:execution_result][:exception_encountered].message.should == "expected: 2,\n got: 1 (using ==)"
30
+ end
31
+ it "should capture exceptions" do
32
+ group = RSpec::Core::ExampleGroup.describe
33
+ example = group.iso_it('example') { raise "FOO" }
34
+
35
+ group.run
36
+ example.metadata[:execution_result][:exception_encountered].message.should == "FOO"
37
+ end
38
+ end
39
+ context "complicated conditions" do
40
+ RSpec::Core::ExampleGroup.module_eval do
41
+ def once_then_raise_error
42
+ $test_counter ||= 0
43
+ $test_counter += 1
44
+ raise "In the same process" if $test_counter > 1
45
+ return true
46
+ end
47
+ end
48
+ it "should raise error if run in the same process" do
49
+ group = RSpec::Core::ExampleGroup.describe do
50
+ $test_counter = 0
51
+ example {once_then_raise_error}
52
+ example {once_then_raise_error}
53
+ end
54
+ group.run
55
+ group.examples[0].metadata[:execution_result][:exception_encountered].should be_nil
56
+ group.examples[1].metadata[:execution_result][:exception_encountered].message.should == "In the same process"
57
+ end
58
+ it "should not raise if ran in isolation" do
59
+ group = RSpec::Core::ExampleGroup.describe do
60
+ $test_counter = 0
61
+ iso_it {once_then_raise_error}
62
+ iso_it {once_then_raise_error}
63
+ example {once_then_raise_error}
64
+ end
65
+ group.run
66
+ group.examples[0].metadata[:execution_result][:exception_encountered].should be_nil
67
+ group.examples[0].metadata[:execution_result][:exception_encountered].should be_nil
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,83 @@
1
+ require 'rspec/isolation'
2
+
3
+ # Dir['./spec/support/**/*.rb'].map {|f| require f}
4
+
5
+ module RSpec
6
+ module Core
7
+ module Matchers
8
+ def fail
9
+ raise_error(::RSpec::Expectations::ExpectationNotMetError)
10
+ end
11
+
12
+ def fail_with(message)
13
+ raise_error(::RSpec::Expectations::ExpectationNotMetError, message)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ class NullObject
20
+ def method_missing(method, *args, &block)
21
+ # ignore
22
+ end
23
+ end
24
+
25
+ def sandboxed(&block)
26
+ begin
27
+ @orig_config = RSpec.configuration
28
+ @orig_world = RSpec.world
29
+ new_config = RSpec::Core::Configuration.new
30
+ new_config.include(RSpec::Matchers)
31
+ new_world = RSpec::Core::World.new(new_config)
32
+ RSpec.instance_variable_set(:@configuration, new_config)
33
+ RSpec.instance_variable_set(:@world, new_world)
34
+ object = Object.new
35
+ object.extend(RSpec::Core::ObjectExtensions)
36
+ object.extend(RSpec::Core::SharedExampleGroup)
37
+
38
+ (class << RSpec::Core::ExampleGroup; self; end).class_eval do
39
+ alias_method :orig_run, :run
40
+ def run(reporter=nil)
41
+ @orig_mock_space = RSpec::Mocks::space
42
+ RSpec::Mocks::space = RSpec::Mocks::Space.new
43
+ orig_run(reporter || NullObject.new)
44
+ ensure
45
+ RSpec::Mocks::space = @orig_mock_space
46
+ end
47
+ end
48
+
49
+ object.instance_eval(&block)
50
+ ensure
51
+ (class << RSpec::Core::ExampleGroup; self; end).class_eval do
52
+ remove_method :run
53
+ alias_method :run, :orig_run
54
+ remove_method :orig_run
55
+ end
56
+
57
+ RSpec.instance_variable_set(:@configuration, @orig_config)
58
+ RSpec.instance_variable_set(:@world, @orig_world)
59
+ end
60
+ end
61
+
62
+ def in_editor?
63
+ ENV.has_key?('TM_MODE') || ENV.has_key?('EMACS') || ENV.has_key?('VIM')
64
+ end
65
+
66
+ RSpec.configure do |c|
67
+ c.color_enabled = !in_editor?
68
+ c.filter_run :focused => true
69
+ c.run_all_when_everything_filtered = true
70
+ c.filter_run_excluding :ruby => lambda {|version|
71
+ case version.to_s
72
+ when "!jruby"
73
+ RUBY_ENGINE != "jruby"
74
+ when /^> (.*)/
75
+ !(RUBY_VERSION.to_s > $1)
76
+ else
77
+ !(RUBY_VERSION.to_s =~ /^#{version.to_s}/)
78
+ end
79
+ }
80
+ c.around do |example|
81
+ sandboxed { example.run }
82
+ end
83
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-isolation
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Kevin Fu
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-10-16 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: bundler
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 0
31
+ - 0
32
+ - rc
33
+ - 5
34
+ version: 1.0.0.rc.5
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ segments:
46
+ - 2
47
+ - 0
48
+ - 0
49
+ version: 2.0.0
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: Make rspec examples executed in separated processes. Especially used in framework development.
53
+ email:
54
+ - corntrace@gmail.com
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - README.mkd
65
+ - Rakefile
66
+ - lib/rspec/isolation.rb
67
+ - lib/rspec/isolation/core_ext.rb
68
+ - lib/rspec/isolation/version.rb
69
+ - rspec-isolation.gemspec
70
+ - spec/rspec_isolation_spec.rb
71
+ - spec/spec_helper.rb
72
+ has_rdoc: true
73
+ homepage: http://rubygems.org/gems/rspec-isolation
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 1
96
+ - 3
97
+ - 6
98
+ version: 1.3.6
99
+ requirements: []
100
+
101
+ rubyforge_project:
102
+ rubygems_version: 1.3.7
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Make rspec examples executed in separated processes.
106
+ test_files: []
107
+