parallel_rspec 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1df19064c0de30bc25fe56463553c51354d38319
4
+ data.tar.gz: 41552dac37c0fff6e8f04612493d4593ea68aa25
5
+ SHA512:
6
+ metadata.gz: 7458d557acec03dfce6c25e3908d363b30c69d3bb896c1fd633471c25ce32f862d8b3b23ab4419247cd9730def619578fee671a1fa797881fc72372dacdd39d1
7
+ data.tar.gz: b7d6e86fe55ddc5e926c5452243e9e035c0dc6024f8570d6d4bb306b32a1dbf42cc0dd19fad10ac25ec47133dbe3c7dd7c96f6afff8a4e8e7008ebc672633cf5
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parallel_rspec.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Will Bryant
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,50 @@
1
+ # ParallelRSpec
2
+
3
+ This gem lets you run your RSpec examples in parallel across across your CPUs. Each worker gets its own database to avoid conflicts.
4
+
5
+ ## Installation
6
+
7
+ Add this to your application's Gemfile:
8
+
9
+ ```ruby
10
+ group :development, :test do
11
+ gem 'parallel_rspec'
12
+ end
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install parallel_rspec
22
+
23
+ This version of ParallelRSpec has been tested with RSpec 3.3.
24
+
25
+ ## Usage
26
+
27
+ By default, ParallelRSpec will use two workers. If you would like to use more, set an environment variable:
28
+
29
+ $ export WORKERS=2
30
+
31
+ ParallelRSpec runs each worker with its own copy of the test database to avoid locking and deadlocking problems. To create these and populate them with your schema, run:
32
+
33
+ $ bundle exec db:parallel:create db:parallel:prepare
34
+
35
+ ParallelRSpec will automatically make the database name for each worker based on the name you used for the `test` environment in `config/database.yml`. For example, if your normal `test` database is `foo_test`, worker 1 will keep using `foo_test` but worker 2's database will be `foo_test2`.
36
+
37
+ You're then ready to run specs in parallel:
38
+
39
+ $ bundle exec prspec spec/my_spec.rb spec/another_spec.rb
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at https://github.com/willbryant/parallel_rspec.
44
+
45
+
46
+ ## License
47
+
48
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
49
+
50
+ Copyright (c) Powershop New Zealand Ltd, 2015.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'parallel_rspec'
3
+ ParallelRSpec::Runner.invoke
@@ -0,0 +1,4 @@
1
+ require "parallel_rspec/version"
2
+ require "parallel_rspec/workers"
3
+ require "parallel_rspec/runner"
4
+ require "parallel_rspec/railtie"
@@ -0,0 +1,11 @@
1
+ require 'rails'
2
+
3
+ module ParallelRSpec
4
+ class Railtie < Rails::Railtie
5
+ railtie_name :parallel_rspec
6
+
7
+ rake_tasks do
8
+ load "parallel_rspec/tasks.rake"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,80 @@
1
+ require 'rspec/core'
2
+
3
+ module RSpec
4
+ module Core
5
+ class ExampleGroup
6
+ def self.any_context_hooks?
7
+ # unfortunately the public HookCollection API doesn't make this information available, so we have to grab it from internals
8
+ descendants.any? do |group| # despite the name, descendents includes self
9
+ group.hooks.send(:matching_hooks_for, :before, :context, group).present? ||
10
+ group.hooks.send(:matching_hooks_for, :after, :context, group).present?
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ module ParallelRSpec
18
+ class Runner < RSpec::Core::Runner
19
+ # Runs the suite of specs and exits the process with an appropriate exit
20
+ # code.
21
+ def self.invoke
22
+ status = run(ARGV, $stderr, $stdout).to_i
23
+ exit(status) if status != 0
24
+ end
25
+
26
+ # Run a suite of RSpec examples. Does not exit.
27
+ #
28
+ # This is used internally by RSpec to run a suite, but is available
29
+ # for use by any other automation tool.
30
+ #
31
+ # If you want to run this multiple times in the same process, and you
32
+ # want files like `spec_helper.rb` to be reloaded, be sure to load `load`
33
+ # instead of `require`.
34
+ #
35
+ # @param args [Array] command-line-supported arguments
36
+ # @param err [IO] error stream
37
+ # @param out [IO] output stream
38
+ # @return [Fixnum] exit status code. 0 if all specs passed,
39
+ # or the configured failure exit code (1 by default) if specs
40
+ # failed.
41
+ def self.run(args, err=$stderr, out=$stdout)
42
+ RSpec::Core::Runner.trap_interrupt
43
+ options = RSpec::Core::ConfigurationOptions.new(args)
44
+ new(options).run(err, out)
45
+ end
46
+
47
+ # Runs the provided example groups.
48
+ #
49
+ # @param example_groups [Array<RSpec::Core::ExampleGroup>] groups to run
50
+ # @return [Fixnum] exit status code. 0 if all specs passed,
51
+ # or the configured failure exit code (1 by default) if specs
52
+ # failed.
53
+ def run_specs(example_groups)
54
+ @configuration.reporter.report(@world.example_count(example_groups)) do |reporter|
55
+ @configuration.with_suite_hooks do
56
+ with_context_hooks, without_context_hooks = example_groups.partition(&:any_context_hooks?)
57
+ success = run_in_parallel(without_context_hooks, reporter)
58
+ success &&= with_context_hooks.map { |g| g.run(reporter) }.all?
59
+ success ? 0 : @configuration.failure_exit_code
60
+ end
61
+ end
62
+ end
63
+
64
+ def run_in_parallel(example_groups, reporter)
65
+ workers = Workers.new
66
+ workers.run_test_workers do |worker|
67
+ index = 0
68
+ RSpec.world.filtered_examples.each do |group, examples|
69
+ examples.reject! do |example|
70
+ index += 1
71
+ (index % workers.number_of_workers) != (worker % workers.number_of_workers)
72
+ end
73
+ end
74
+ success = example_groups.map { |g| g.run(reporter) }.all?
75
+ reporter.finish
76
+ success
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,59 @@
1
+ require 'lib/parallel_workers.rb'
2
+
3
+ db_namespace = namespace :db do
4
+ namespace :parallel do
5
+ # desc "Creates the test database"
6
+ task :create => [:load_config] do
7
+ ParallelWorkers.new.run_test_workers do |worker|
8
+ ActiveRecord::Tasks::DatabaseTasks.create ActiveRecord::Base.configurations['test']
9
+ end
10
+ end
11
+
12
+ # desc "Empty the test database"
13
+ task :purge => %w(environment load_config) do
14
+ ParallelWorkers.new.run_test_workers do |worker|
15
+ ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test']
16
+ end
17
+ end
18
+
19
+ # desc "Recreate the test database from an existent schema.rb file"
20
+ task :load_schema => %w(db:parallel:purge) do
21
+ should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
22
+ begin
23
+ ParallelWorkers.new.run_test_workers do |worker|
24
+ ActiveRecord::Schema.verbose = false
25
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA']
26
+ end
27
+ ensure
28
+ if should_reconnect
29
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
30
+ end
31
+ end
32
+ end
33
+
34
+ # desc "Recreate the test database from an existent structure.sql file"
35
+ task :load_structure => %w(db:parallel:purge) do
36
+ ParallelWorkers.new.run_test_workers do |worker|
37
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA']
38
+ end
39
+ end
40
+
41
+ # desc "Recreate the test database from the current schema"
42
+ task :load do
43
+ db_namespace["db:parallel:purge"].invoke
44
+ case ActiveRecord::Base.schema_format
45
+ when :ruby
46
+ db_namespace["parallel:load_schema"].invoke
47
+ when :sql
48
+ db_namespace["parallel:load_structure"].invoke
49
+ end
50
+ end
51
+
52
+ # desc "Check for pending migrations and load the test schema"
53
+ task :prepare => %w(environment load_config) do
54
+ unless ActiveRecord::Base.configurations.blank?
55
+ db_namespace['parallel:load'].invoke
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module ParallelRSpec
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,41 @@
1
+ module ParallelRSpec
2
+ class Workers
3
+ def self.number_of_workers
4
+ workers = ENV['WORKERS'].to_i
5
+ workers = 4 if workers.zero?
6
+ workers
7
+ end
8
+
9
+ attr_reader :number_of_workers
10
+
11
+ def initialize(number_of_workers = Workers.number_of_workers)
12
+ @number_of_workers = number_of_workers
13
+ end
14
+
15
+ def run_test_workers
16
+ children = (1..number_of_workers).collect do |worker|
17
+ fork do
18
+ establish_test_database_connection(worker)
19
+ yield worker
20
+ end
21
+ end
22
+
23
+ verify_children(children)
24
+ end
25
+
26
+ def establish_test_database_connection(worker)
27
+ ENV['TEST_ENV_NUMBER'] = worker.to_s
28
+ ActiveRecord::Base.configurations['test']['database'] << worker.to_s unless worker.zero?
29
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
30
+ end
31
+
32
+ def verify_children(children)
33
+ results = children.collect { |pid| Process.wait2(pid).last }.reject(&:success?)
34
+
35
+ unless results.empty?
36
+ STDERR.puts "\n#{results.size} worker#{'s' unless results.size == 1} failed"
37
+ exit 1
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parallel_rspec/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "parallel_rspec"
8
+ spec.version = ParallelRSpec::VERSION
9
+ spec.authors = ["Will Bryant, Powershop New Zealand Ltd"]
10
+ spec.email = ["will.bryant@gmail.com"]
11
+
12
+ spec.summary = %q{This gem lets you run your RSpec examples in parallel across across your CPUs..}
13
+ spec.description = %q{This gem lets you run your RSpec examples in parallel across across your CPUs. Each worker automatically gets its own database to avoid conflicts.}
14
+ spec.homepage = "https://github.com/willbryant/parallel_rspec"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parallel_rspec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Will Bryant, Powershop New Zealand Ltd
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: This gem lets you run your RSpec examples in parallel across across your
42
+ CPUs. Each worker automatically gets its own database to avoid conflicts.
43
+ email:
44
+ - will.bryant@gmail.com
45
+ executables:
46
+ - prspec
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - ".travis.yml"
52
+ - Gemfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - exe/prspec
57
+ - lib/parallel_rspec.rb
58
+ - lib/parallel_rspec/railtie.rb
59
+ - lib/parallel_rspec/runner.rb
60
+ - lib/parallel_rspec/tasks.rake
61
+ - lib/parallel_rspec/version.rb
62
+ - lib/parallel_rspec/workers.rb
63
+ - parallel_rspec.gemspec
64
+ homepage: https://github.com/willbryant/parallel_rspec
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: This gem lets you run your RSpec examples in parallel across across your
88
+ CPUs..
89
+ test_files: []