rspec-parallel 2.14.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/rspec ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'rspec/autorun'
5
+ rescue LoadError
6
+ $stderr.puts <<-EOS
7
+ #{'*'*50}
8
+ Could not find 'rspec/autorun'
9
+
10
+ This may happen if you're using rubygems as your package manager, but it is not
11
+ being required through some mechanism before executing the rspec command.
12
+
13
+ You may need to do one of the following in your shell:
14
+
15
+ # for bash/zsh
16
+ export RUBYOPT=rubygems
17
+
18
+ # for csh, etc.
19
+ set RUBYOPT=rubygems
20
+
21
+ For background, please see http://gist.github.com/54177.
22
+ #{'*'*50}
23
+ EOS
24
+ exit(1)
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec/core'
2
+ RSpec::Parallel::Runner.autorun
@@ -0,0 +1,44 @@
1
+ module RSpec
2
+ module Parallel
3
+ class CommandLine < RSpec::Core::CommandLine
4
+ def initialize(options, configuration=RSpec::configuration, world=RSpec::world)
5
+ if Array === options
6
+ options = ConfigurationOptions.new(options)
7
+ options.parse_options
8
+ end
9
+ @options = options
10
+ @configuration = configuration
11
+ @world = world
12
+ end
13
+
14
+ # Configures and runs a suite
15
+ #
16
+ # @param [IO] err
17
+ # @param [IO] out
18
+ def run_parallel(err, out)
19
+ @configuration.error_stream = err
20
+ @configuration.output_stream ||= out
21
+ @options.configure(@configuration)
22
+ @configuration.load_spec_files
23
+ @world.announce_filters
24
+
25
+ @configuration.reporter.report(@world.example_count, @configuration.randomize? ? @configuration.seed : nil) do |reporter|
26
+ begin
27
+ @configuration.run_hook(:before, :suite)
28
+ group_threads = RSpec::Parallel::ExampleGroupThreadRunner.new(@configuration.thread_maximum)
29
+ @world.example_groups.ordered.map {|g| group_threads.run(g, reporter)}
30
+ group_threads.wait_for_completion
31
+
32
+ @world.example_groups.all? do |g|
33
+ result_for_this_group = g.filtered_examples.all? { |example| example.metadata[:execution_result].exception.nil? }
34
+ results_for_descendants = g.children.all? { |child| child.filtered_examples.all? { |example| example.metadata[:execution_result].exception.nil? } }
35
+ result_for_this_group && results_for_descendants
36
+ end ? 0 : @configuration.failure_exit_code
37
+ ensure
38
+ @configuration.run_hook(:after, :suite)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module RSpec
2
+ module Parallel
3
+ class Configuration < RSpec::Core::Configuration
4
+ attr_accessor :thread_maximum
5
+
6
+ def initialize
7
+ super
8
+ @thread_maximum = 1
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module RSpec
2
+ module Parallel
3
+ class ConfigurationOptions < RSpec::Core::ConfigurationOptions
4
+ UNFORCED_OPTIONS = [
5
+ :requires, :profile, :drb, :libs, :files_or_directories_to_run,
6
+ :full_description, :full_backtrace, :tty, :thread_maximum
7
+ ].to_set
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,49 @@
1
+ module RSpec
2
+ module Parallel
3
+ # ExampleGroupThreadRunner is a class used to execute [ExampleGroup]
4
+ # classes in parallel as part of rspec-core. When running in parallel
5
+ # the order of example groups will not be honoured.
6
+ # This class is used to ensure that we have a way of keeping track of
7
+ # the number of threads being created and preventing utilization of
8
+ # more than the specified number
9
+ # Additionally, this class will contain a mutex used to prevent access
10
+ # to shared variables within sub-threads
11
+ class ExampleGroupThreadRunner
12
+ attr_accessor :thread_array, :max_threads, :mutex, :used_threads
13
+
14
+ # Creates a new instance of ExampleGroupThreadRunner.
15
+ # @param max_threads [Integer] the maximum limit of threads that can be used
16
+ # @param mutex [Mutex] a semaphore used to prevent access to shared variables in
17
+ # sub-threads such as those used by [ExampleThreadRunner]
18
+ # @param used_threads [Integer] the current number of threads being used
19
+ def initialize(max_threads = 1, mutex = Mutex.new, used_threads = 0)
20
+ @max_threads = max_threads
21
+ @mutex = mutex
22
+ @used_threads = used_threads
23
+ @thread_array = []
24
+ end
25
+
26
+ # Method will run an [ExampleGroup] inside a [Thread] to prevent blocking
27
+ # execution. The new [Thread] is added to an array for tracking and
28
+ # will automatically remove itself when done
29
+ # @param example_group [ExampleGroup] the group to be run inside a [Thread]
30
+ # @param reporter [Reporter] the passed in reporting class used for
31
+ # tracking
32
+ def run(example_group, reporter)
33
+ @thread_array.push Thread.start {
34
+ example_group.run_parallel(reporter, @max_threads, @mutex, @used_threads)
35
+ @thread_array.delete Thread.current
36
+ }
37
+ end
38
+
39
+ # Method will wait for all threads to complete. On completion threads
40
+ # remove themselves from the @thread_array so an empty array means they
41
+ # completed
42
+ def wait_for_completion
43
+ @thread_array.each do |t|
44
+ t.join
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,55 @@
1
+ module RSpec
2
+ module Parallel
3
+ # ExampleThreadRunner is a class used to execute [Example] classes in
4
+ # parallel as part of rspec-core. When running in parallel the order
5
+ # of examples will not be honoured.
6
+ # This class is used to ensure that we have a way of keeping track of
7
+ # the number of threads being created and preventing utilization of
8
+ # more than the specified number
9
+ class ExampleThreadRunner
10
+ attr_accessor :num_threads, :thread_array, :used_threads
11
+
12
+ # Creates a new instance of ExampleThreadRunner.
13
+ # @param num_threads [Integer] the maximum limit of threads that can be used
14
+ # @param used_threads [Integer] the current number of threads being used
15
+ def initialize(num_threads, used_threads)
16
+ @num_threads = num_threads
17
+ @thread_array = []
18
+ @used_threads = used_threads
19
+ end
20
+
21
+ # Method will check global utilization of threads and if that number is
22
+ # at or over the allocated maximum it will wait until a thread is available
23
+ def wait_for_available_thread
24
+ while @used_threads.to_i >= @num_threads.to_i
25
+ sleep 0.1
26
+ end
27
+ end
28
+
29
+ # Method will run the specified example within an available thread or
30
+ # will wait for a thread to become available if none currently are
31
+ # @param example [Example] the example to be executed in a [Thread]
32
+ # @param instance the instance of an ExampleGroup subclass
33
+ # @param reporter [Reporter] the passed in reporting class used for
34
+ # tracking
35
+ def run(example, instance, reporter)
36
+ wait_for_available_thread
37
+ @thread_array.push Thread.start {
38
+ example.run(instance, reporter)
39
+ @thread_array.delete Thread.current # remove from local scope
40
+ @used_threads -= 1
41
+ }
42
+ @used_threads += 1
43
+ end
44
+
45
+ # Method will wait for all threads to complete. On completion threads
46
+ # remove themselves from the @thread_array so an empty array means they
47
+ # completed
48
+ def wait_for_completion
49
+ @thread_array.each do |t|
50
+ t.join
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,213 @@
1
+ module RSpec
2
+ module Parallel
3
+ class Parser < RSpec::Core::Parser
4
+ def parser(options)
5
+ OptionParser.new do |parser|
6
+ parser.banner = "Usage: rspec [options] [files or directories]\n\n"
7
+
8
+ parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dir|
9
+ options[:libs] ||= []
10
+ options[:libs] << dir
11
+ end
12
+
13
+ parser.on('-r', '--require PATH', 'Require a file.') do |path|
14
+ options[:requires] ||= []
15
+ options[:requires] << path
16
+ end
17
+
18
+ parser.on('-O', '--options PATH', 'Specify the path to a custom options file.') do |path|
19
+ options[:custom_options_file] = path
20
+ end
21
+
22
+ parser.on('--order TYPE[:SEED]', 'Run examples by the specified order type.',
23
+ ' [defined] examples and groups are run in the order they are defined',
24
+ ' [rand] randomize the order of groups and examples',
25
+ ' [random] alias for rand',
26
+ ' [random:SEED] e.g. --order random:123') do |o|
27
+ options[:order] = o
28
+ end
29
+
30
+ parser.on('--seed SEED', Integer, 'Equivalent of --order rand:SEED.') do |seed|
31
+ options[:order] = "rand:#{seed}"
32
+ end
33
+
34
+ parser.on('--fail-fast', 'Abort the run on first failure.') do |o|
35
+ options[:fail_fast] = true
36
+ end
37
+
38
+ parser.on('--no-fail-fast', 'Do not abort the run on first failure.') do |o|
39
+ options[:fail_fast] = false
40
+ end
41
+
42
+ parser.on('--failure-exit-code CODE', Integer, 'Override the exit code used when there are failing specs.') do |code|
43
+ options[:failure_exit_code] = code
44
+ end
45
+
46
+ parser.on('--dry-run', 'Print the formatter output of your suite without',
47
+ ' running any examples or hooks') do |o|
48
+ options[:dry_run] = true
49
+ end
50
+
51
+ parser.on('-X', '--[no-]drb', 'Run examples via DRb.') do |o|
52
+ options[:drb] = o
53
+ end
54
+
55
+ parser.on('--drb-port PORT', 'Port to connect to the DRb server.') do |o|
56
+ options[:drb_port] = o.to_i
57
+ end
58
+
59
+ parser.on('--init', 'Initialize your project with RSpec.') do |cmd|
60
+ RSpec::Support.require_rspec_core "project_initializer"
61
+ ProjectInitializer.new.run
62
+ exit
63
+ end
64
+
65
+ parser.separator("\n **** Output ****\n\n")
66
+
67
+ parser.on('-f', '--format FORMATTER', 'Choose a formatter.',
68
+ ' [p]rogress (default - dots)',
69
+ ' [d]ocumentation (group and example names)',
70
+ ' [h]tml',
71
+ ' [j]son',
72
+ ' custom formatter class name') do |o|
73
+ options[:formatters] ||= []
74
+ options[:formatters] << [o]
75
+ end
76
+
77
+ parser.on('-o', '--out FILE',
78
+ 'Write output to a file instead of $stdout. This option applies',
79
+ ' to the previously specified --format, or the default format',
80
+ ' if no format is specified.'
81
+ ) do |o|
82
+ options[:formatters] ||= [['progress']]
83
+ options[:formatters].last << o
84
+ end
85
+
86
+ parser.on('--deprecation-out FILE', 'Write deprecation warnings to a file instead of $stderr.') do |file|
87
+ options[:deprecation_stream] = file
88
+ end
89
+
90
+ parser.on('-b', '--backtrace', 'Enable full backtrace.') do |o|
91
+ options[:full_backtrace] = true
92
+ end
93
+
94
+ parser.on('-c', '--[no-]color', '--[no-]colour', 'Enable color in the output.') do |o|
95
+ options[:color] = o
96
+ end
97
+
98
+ parser.on('-p', '--[no-]profile [COUNT]', 'Enable profiling of examples and list the slowest examples (default: 10).') do |argument|
99
+ options[:profile_examples] = if argument.nil?
100
+ true
101
+ elsif argument == false
102
+ false
103
+ else
104
+ begin
105
+ Integer(argument)
106
+ rescue ArgumentError
107
+ RSpec.warning "Non integer specified as profile count, seperate " +
108
+ "your path from options with -- e.g. " +
109
+ "`rspec --profile -- #{argument}`",
110
+ :call_site => nil
111
+ true
112
+ end
113
+ end
114
+ end
115
+
116
+ parser.on('-w', '--warnings', 'Enable ruby warnings') do
117
+ $VERBOSE = true
118
+ end
119
+
120
+ parser.on('--parallel-test NUMBER', 'Run the tests with the specified number of parallel threads (default: 1).') do |n|
121
+ options[:thread_maximum] = if !n.nil?
122
+ begin
123
+ Integer(n)
124
+ rescue ArgumentError
125
+ RSpec.warning "Non integer specified as number of parallel threads, seperate " +
126
+ "your path from options with a space e.g. " +
127
+ "`rspec --parallel-test #{n}`",
128
+ :call_site => nil
129
+ 1
130
+ end
131
+ end
132
+ end
133
+
134
+ parser.separator <<-FILTERING
135
+
136
+ **** Filtering/tags ****
137
+
138
+ In addition to the following options for selecting specific files, groups,
139
+ or examples, you can select a single example by appending the line number to
140
+ the filename:
141
+
142
+ rspec path/to/a_spec.rb:37
143
+
144
+ FILTERING
145
+
146
+ parser.on('-P', '--pattern PATTERN', 'Load files matching pattern (default: "spec/**/*_spec.rb").') do |o|
147
+ options[:pattern] = o
148
+ end
149
+
150
+ parser.on('-e', '--example STRING', "Run examples whose full nested names include STRING (may be",
151
+ " used more than once)") do |o|
152
+ (options[:full_description] ||= []) << Regexp.compile(Regexp.escape(o))
153
+ end
154
+
155
+ parser.on('-t', '--tag TAG[:VALUE]',
156
+ 'Run examples with the specified tag, or exclude examples',
157
+ 'by adding ~ before the tag.',
158
+ ' - e.g. ~slow',
159
+ ' - TAG is always converted to a symbol') do |tag|
160
+ filter_type = tag =~ /^~/ ? :exclusion_filter : :inclusion_filter
161
+
162
+ name,value = tag.gsub(/^(~@|~|@)/, '').split(':',2)
163
+ name = name.to_sym
164
+
165
+ options[filter_type] ||= {}
166
+ options[filter_type][name] = case value
167
+ when nil then true # The default value for tags is true
168
+ when 'true' then true
169
+ when 'false' then false
170
+ when 'nil' then nil
171
+ when /^:/ then value[1..-1].to_sym
172
+ when /^\d+$/ then Integer(value)
173
+ when /^\d+.\d+$/ then Float(value)
174
+ else
175
+ value
176
+ end
177
+ end
178
+
179
+ parser.on('--default-path PATH', 'Set the default path where RSpec looks for examples (can',
180
+ ' be a path to a file or a directory).') do |path|
181
+ options[:default_path] = path
182
+ end
183
+
184
+ parser.separator("\n **** Utility ****\n\n")
185
+
186
+ parser.on('-v', '--version', 'Display the version.') do
187
+ puts RSpec::Core::Version::STRING
188
+ exit
189
+ end
190
+
191
+ # these options would otherwise be confusing to users, so we forcibly prevent them from executing
192
+ # --I is too similar to -I
193
+ # -d was a shorthand for --debugger, which is removed, but now would trigger --default-path
194
+ invalid_options = %w[-d --I]
195
+
196
+ parser.on_tail('-h', '--help', "You're looking at it.") do
197
+ # removing the blank invalid options from the output
198
+ puts parser.to_s.gsub(/^\s+(#{invalid_options.join('|')})\s*$\n/,'')
199
+ exit
200
+ end
201
+
202
+ # this prevents usage of the invalid_options
203
+ invalid_options.each do |option|
204
+ parser.on(option) do
205
+ raise OptionParser::InvalidOption.new
206
+ end
207
+ end
208
+
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,51 @@
1
+ module RSpec
2
+ module Parallel
3
+ class Runner < RSpec::Core::Runner
4
+ # Run a suite of RSpec examples.
5
+ #
6
+ # This is used internally by RSpec to run a suite, but is available
7
+ # for use by any other automation tool.
8
+ #
9
+ # If you want to run this multiple times in the same process, and you
10
+ # want files like spec_helper.rb to be reloaded, be sure to load `load`
11
+ # instead of `require`.
12
+ #
13
+ # #### Parameters
14
+ # * +args+ - an array of command-line-supported arguments
15
+ # * +err+ - error stream (Default: $stderr)
16
+ # * +out+ - output stream (Default: $stdout)
17
+ #
18
+ # #### Returns
19
+ # * +Fixnum+ - exit status code (0/1)
20
+ def self.run(args, err=$stderr, out=$stdout)
21
+ trap_interrupt
22
+ options = ConfigurationOptions.new(args)
23
+ options.parse_options
24
+
25
+ parallel = (options.options[:thread_maximum].nil?) ? false : true
26
+
27
+ if options.options[:drb]
28
+ require 'rspec/core/drb_command_line'
29
+ begin
30
+ DRbCommandLine.new(options).run(err, out)
31
+ rescue DRb::DRbConnError
32
+ err.puts "No DRb server is running. Running in local process instead ..."
33
+ if parallel
34
+ CommandLine.new(options).run_parallel(err, out)
35
+ else
36
+ CommandLine.new(options).run(err, out)
37
+ end
38
+ end
39
+ else
40
+ if parallel
41
+ CommandLine.new(options).run_parallel(err, out)
42
+ else
43
+ CommandLine.new(options).run(err, out)
44
+ end
45
+ end
46
+ ensure
47
+ RSpec.reset
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ module RSpec
2
+ module Parallel
3
+ # Version information for RSpec Parallel.
4
+ module Version
5
+ # Current version of RSpec Parallel, in semantic versioning format.
6
+ STRING = '2.14.8.1'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,79 @@
1
+ require 'rspec'
2
+ require_rspec = if defined?(require_relative)
3
+ lambda do |path|
4
+ require_relative path
5
+ end
6
+ else
7
+ lambda do |path|
8
+ require "rspec/#{path}"
9
+ end
10
+ end
11
+
12
+ require_rspec['parallel/version']
13
+ require_rspec['parallel/configuration']
14
+ require_rspec['parallel/option_parser']
15
+ require_rspec['parallel/configuration_options']
16
+ require_rspec['parallel/example_thread_runner']
17
+ require_rspec['parallel/example_group_thread_runner']
18
+ require_rspec['parallel/runner']
19
+
20
+ module RSpec
21
+ # Returns the global [Configuration](RSpec/Parallel/Configuration) object. While you
22
+ # _can_ use this method to access the configuration, the more common
23
+ # convention is to use [RSpec.configure](RSpec#configure-class_method).
24
+ #
25
+ # @example
26
+ # RSpec.configuration.drb_port = 1234
27
+ # @see RSpec.configure
28
+ # @see Parallel::Configuration
29
+ def self.configuration
30
+ @configuration ||= begin
31
+ config = RSpec::Parallel::Configuration.new
32
+ config.expose_dsl_globally = true
33
+ config
34
+ end
35
+
36
+ end
37
+
38
+ module Parallel
39
+ class << RSpec::Core::ExampleGroup
40
+ # Runs all the examples in this group in a separate thread for each
41
+ def run_parallel(reporter, num_threads, mutex, used_threads)
42
+ if RSpec.wants_to_quit
43
+ RSpec.clear_remaining_example_groups if top_level?
44
+ return
45
+ end
46
+ reporter.example_group_started(self)
47
+
48
+ begin
49
+ run_before_all_hooks(new)
50
+ example_threads = RSpec::Parallel::ExampleThreadRunner.new(num_threads, used_threads)
51
+ run_examples_parallel(reporter, example_threads, mutex)
52
+ children.ordered.map {|child| child.run_parallel(reporter, num_threads, mutex, used_threads)}
53
+ example_threads.wait_for_completion
54
+ rescue Exception => ex
55
+ RSpec.wants_to_quit = true if fail_fast?
56
+ fail_filtered_examples(ex, reporter)
57
+ ensure
58
+ run_after_all_hooks(new)
59
+ before_all_ivars.clear
60
+ reporter.example_group_finished(self)
61
+ end
62
+ end
63
+
64
+ # @private
65
+ # Runs the examples in this group in a separate thread each
66
+ def run_examples_parallel(reporter, threads, mutex)
67
+ filtered_examples.ordered.map do |example|
68
+ next if RSpec.wants_to_quit
69
+ instance = new
70
+ set_ivars(instance, before_all_ivars)
71
+ mutex.synchronize do
72
+ threads.run(example, instance, reporter)
73
+ end
74
+ RSpec.wants_to_quit = true if fail_fast?
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-parallel
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.14.8.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jason Holt Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.14.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.14.0
30
+ description: Bolt-on gem allowing for parallel execution of examples using rspec's
31
+ framework
32
+ email: bicarbon8@gmail.com
33
+ executables:
34
+ - rspec
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - lib/rspec/parallel.rb
39
+ - lib/rspec/parallel/autorun.rb
40
+ - lib/rspec/parallel/command_line.rb
41
+ - lib/rspec/parallel/configuration.rb
42
+ - lib/rspec/parallel/configuration_options.rb
43
+ - lib/rspec/parallel/example_group_thread_runner.rb
44
+ - lib/rspec/parallel/example_thread_runner.rb
45
+ - lib/rspec/parallel/option_parser.rb
46
+ - lib/rspec/parallel/runner.rb
47
+ - lib/rspec/parallel/version.rb
48
+ - bin/rspec
49
+ homepage: https://github.com/bicarbon8/rspec-parallel.git
50
+ licenses:
51
+ - MIT
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.8.7
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.23
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Parallel rspec execution gem
74
+ test_files: []
75
+ has_rdoc: