parallel_tests 0.4.20 → 0.4.21
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/Readme.md +42 -3
- data/VERSION +1 -1
- data/lib/parallel_specs/spec_failures_logger.rb +25 -0
- data/lib/parallel_specs/spec_logger_base.rb +72 -0
- data/lib/parallel_specs/spec_runtime_logger.rb +3 -59
- data/lib/parallel_specs/spec_summary_logger.rb +47 -0
- data/parallel_tests.gemspec +5 -2
- data/spec/parallel_specs_spec.rb +102 -0
- data/spec/spec_helper.rb +5 -1
- metadata +7 -4
data/Readme.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Speedup Test::Unit + RSpec + Cucumber by running parallel on multiple CPUs(or cores).
|
1
|
+
Speedup Test::Unit + RSpec + Cucumber by running parallel on multiple CPUs (or cores).
|
2
2
|
|
3
3
|
Setup for Rails
|
4
4
|
===============
|
@@ -59,6 +59,7 @@ OR as plugin
|
|
59
59
|
...
|
60
60
|
|
61
61
|
Test just a subfolder (e.g. use one integration server per subfolder)
|
62
|
+
|
62
63
|
rake parallel:test[models]
|
63
64
|
rake parallel:test[something/else]
|
64
65
|
|
@@ -76,10 +77,14 @@ Example output
|
|
76
77
|
|
77
78
|
Took 29.925333 seconds
|
78
79
|
|
79
|
-
|
80
|
+
Spec Loggers
|
81
|
+
===================
|
82
|
+
|
83
|
+
Even process runtimes
|
80
84
|
-----------------
|
81
85
|
|
82
|
-
Log test runtime to give each process the same test runtime
|
86
|
+
Log test runtime to give each process the same test runtime.
|
87
|
+
|
83
88
|
Add to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
|
84
89
|
|
85
90
|
RSpec 1.x:
|
@@ -90,6 +95,36 @@ Add to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
|
|
90
95
|
--format progress
|
91
96
|
--format ParallelSpecs::SpecRuntimeLogger --out tmp/parallel_profile.log
|
92
97
|
|
98
|
+
SpecSummaryLogger
|
99
|
+
--------------------
|
100
|
+
|
101
|
+
This logger stops the different processes overwriting each other's output.
|
102
|
+
|
103
|
+
Add the following to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
|
104
|
+
|
105
|
+
RSpec 1.x:
|
106
|
+
--format progress
|
107
|
+
--format ParallelSpecs::SpecSummaryLogger:tmp/spec_summary.log
|
108
|
+
RSpec >= 2.2:
|
109
|
+
--format progress
|
110
|
+
--format ParallelSpecs::SpecSummaryLogger --out tmp/spec_summary.log
|
111
|
+
|
112
|
+
SpecFailuresLogger
|
113
|
+
-----------------------
|
114
|
+
|
115
|
+
This logger produces command lines for running any failing examples.
|
116
|
+
|
117
|
+
E.g.
|
118
|
+
|
119
|
+
spec /path/to/my_spec.rb -e "should do something"
|
120
|
+
|
121
|
+
Add the following to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
|
122
|
+
|
123
|
+
RSpec 1.x:
|
124
|
+
--format ParallelSpecs::SpecFailuresLogger:tmp/failing_specs.log
|
125
|
+
RSpec >= 2.2:
|
126
|
+
--format ParallelSpecs::SpecFailuresLogger --out tmp/failing_specs.log
|
127
|
+
|
93
128
|
Setup for non-rails
|
94
129
|
===================
|
95
130
|
sudo gem install parallel_tests
|
@@ -98,9 +133,11 @@ Setup for non-rails
|
|
98
133
|
# [Optional] use ENV['TEST_ENV_NUMBER'] inside your tests to select separate db/memcache/etc.
|
99
134
|
|
100
135
|
[optional] Only run selected files & folders:
|
136
|
+
|
101
137
|
parallel_test test/bar test/baz/xxx_text.rb
|
102
138
|
|
103
139
|
Options are:
|
140
|
+
|
104
141
|
-n [PROCESSES] How many processes to use, default: available CPUs
|
105
142
|
-p, --path [PATH] run tests inside this path only
|
106
143
|
--no-sort do not sort files before running them
|
@@ -114,6 +151,7 @@ Options are:
|
|
114
151
|
-h, --help Show this.
|
115
152
|
|
116
153
|
You can run any kind of code with -e / --execute
|
154
|
+
|
117
155
|
parallel_test -n 5 -e 'ruby -e "puts %[hello from process #{ENV[:TEST_ENV_NUMBER.to_s].inspect}]"'
|
118
156
|
hello from process "2"
|
119
157
|
hello from process ""
|
@@ -169,6 +207,7 @@ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-
|
|
169
207
|
- [Levent Ali](http://purebreeze.com/)
|
170
208
|
- [Michael Kintzer](https://github.com/rockrep)
|
171
209
|
- [nathansobo](https://github.com/nathansobo)
|
210
|
+
- [Joe Yates](http://titusd.co.uk)
|
172
211
|
|
173
212
|
[Michael Grosser](http://grosser.it)<br/>
|
174
213
|
michael@grosser.it<br/>
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.21
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'parallel_specs'
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_logger_base')
|
3
|
+
|
4
|
+
class ParallelSpecs::SpecFailuresLogger < ParallelSpecs::SpecLoggerBase
|
5
|
+
def initialize(options, output=nil)
|
6
|
+
super
|
7
|
+
@failed_examples = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def example_failed(example, count, failure)
|
11
|
+
@failed_examples << example
|
12
|
+
end
|
13
|
+
|
14
|
+
def dump_failure(*args)
|
15
|
+
lock_output do
|
16
|
+
@failed_examples.each.with_index do | example, i |
|
17
|
+
spec_file = example.location.scan(/^[^:]+/)[0]
|
18
|
+
spec_file.gsub!(%r(^.*?/spec/), './spec/')
|
19
|
+
@output.puts "#{ParallelSpecs.executable} #{spec_file} -e \"#{example.description}\""
|
20
|
+
end
|
21
|
+
end
|
22
|
+
@output.flush
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'parallel_specs'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rspec/core/formatters/progress_formatter'
|
5
|
+
base = RSpec::Core::Formatters::ProgressFormatter
|
6
|
+
rescue LoadError
|
7
|
+
require 'spec/runner/formatter/progress_bar_formatter'
|
8
|
+
base = Spec::Runner::Formatter::BaseTextFormatter
|
9
|
+
end
|
10
|
+
ParallelSpecs::SpecLoggerBaseBase = base
|
11
|
+
|
12
|
+
class ParallelSpecs::SpecLoggerBase < ParallelSpecs::SpecLoggerBaseBase
|
13
|
+
def initialize(options, output=nil)
|
14
|
+
output ||= options # rspec 2 has output as first argument
|
15
|
+
|
16
|
+
if String === output
|
17
|
+
FileUtils.mkdir_p(File.dirname(output))
|
18
|
+
File.open(output, 'w'){} # overwrite previous results
|
19
|
+
@output = File.open(output, 'a')
|
20
|
+
elsif File === output
|
21
|
+
output.close # close file opened with 'w'
|
22
|
+
@output = File.open(output.path, 'a')
|
23
|
+
else
|
24
|
+
@output = output
|
25
|
+
end
|
26
|
+
|
27
|
+
@failed_examples = [] # only needed for rspec 2
|
28
|
+
end
|
29
|
+
|
30
|
+
def example_started(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def example_passed(example)
|
34
|
+
end
|
35
|
+
|
36
|
+
def example_pending(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def example_failed(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_dump(*args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def dump_summary(*args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def dump_pending(*args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def dump_failure(*args)
|
52
|
+
end
|
53
|
+
|
54
|
+
#stolen from Rspec
|
55
|
+
def close
|
56
|
+
@output.close if (IO === @output) & (@output != $stdout)
|
57
|
+
end
|
58
|
+
|
59
|
+
# do not let multiple processes get in each others way
|
60
|
+
def lock_output
|
61
|
+
if File === @output
|
62
|
+
begin
|
63
|
+
@output.flock File::LOCK_EX
|
64
|
+
yield
|
65
|
+
ensure
|
66
|
+
@output.flock File::LOCK_UN
|
67
|
+
end
|
68
|
+
else
|
69
|
+
yield
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,31 +1,10 @@
|
|
1
1
|
require 'parallel_specs'
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_logger_base')
|
2
3
|
|
3
|
-
|
4
|
-
require 'rspec/core/formatters/progress_formatter'
|
5
|
-
base = RSpec::Core::Formatters::ProgressFormatter
|
6
|
-
rescue LoadError
|
7
|
-
require 'spec/runner/formatter/progress_bar_formatter'
|
8
|
-
base = Spec::Runner::Formatter::BaseTextFormatter
|
9
|
-
end
|
10
|
-
ParallelSpecs::SpecRuntimeLoggerBase = base
|
11
|
-
|
12
|
-
class ParallelSpecs::SpecRuntimeLogger < ParallelSpecs::SpecRuntimeLoggerBase
|
4
|
+
class ParallelSpecs::SpecRuntimeLogger < ParallelSpecs::SpecLoggerBase
|
13
5
|
def initialize(options, output=nil)
|
14
|
-
|
15
|
-
|
16
|
-
if String === output
|
17
|
-
FileUtils.mkdir_p(File.dirname(output))
|
18
|
-
File.open(output, 'w'){} # overwrite previous results
|
19
|
-
@output = File.open(output, 'a')
|
20
|
-
elsif File === output
|
21
|
-
output.close # close file opened with 'w'
|
22
|
-
@output = File.open(output.path, 'a')
|
23
|
-
else
|
24
|
-
@output = output
|
25
|
-
end
|
26
|
-
|
6
|
+
super
|
27
7
|
@example_times = Hash.new(0)
|
28
|
-
@failed_examples = [] # only needed for rspec 2
|
29
8
|
end
|
30
9
|
|
31
10
|
def example_started(*args)
|
@@ -46,39 +25,4 @@ class ParallelSpecs::SpecRuntimeLogger < ParallelSpecs::SpecRuntimeLoggerBase
|
|
46
25
|
@output.flush
|
47
26
|
end
|
48
27
|
|
49
|
-
# stubs so that rspec doe not crash
|
50
|
-
|
51
|
-
def example_pending(*args)
|
52
|
-
end
|
53
|
-
|
54
|
-
def example_failed(*args)
|
55
|
-
end
|
56
|
-
|
57
|
-
def dump_summary(*args)
|
58
|
-
end
|
59
|
-
|
60
|
-
def dump_pending(*args)
|
61
|
-
end
|
62
|
-
|
63
|
-
def dump_failure(*args)
|
64
|
-
end
|
65
|
-
|
66
|
-
#stolen from Rspec
|
67
|
-
def close
|
68
|
-
@output.close if (IO === @output) & (@output != $stdout)
|
69
|
-
end
|
70
|
-
|
71
|
-
# do not let multiple processes get in each others way
|
72
|
-
def lock_output
|
73
|
-
if File === @output
|
74
|
-
begin
|
75
|
-
@output.flock File::LOCK_EX
|
76
|
-
yield
|
77
|
-
ensure
|
78
|
-
@output.flock File::LOCK_UN
|
79
|
-
end
|
80
|
-
else
|
81
|
-
yield
|
82
|
-
end
|
83
|
-
end
|
84
28
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'parallel_specs'
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_logger_base')
|
3
|
+
|
4
|
+
class ParallelSpecs::SpecSummaryLogger < ParallelSpecs::SpecLoggerBase
|
5
|
+
def initialize(options, output=nil)
|
6
|
+
super
|
7
|
+
@passed_examples = []
|
8
|
+
@pending_examples = []
|
9
|
+
@failed_examples = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def example_passed(example)
|
13
|
+
@passed_examples << example
|
14
|
+
end
|
15
|
+
|
16
|
+
def example_pending(*args)
|
17
|
+
@pending_examples << args
|
18
|
+
end
|
19
|
+
|
20
|
+
def example_failed(example, count, failure)
|
21
|
+
@failed_examples << failure
|
22
|
+
end
|
23
|
+
|
24
|
+
def dump_summary(duration, example_count, failure_count, pending_count)
|
25
|
+
lock_output do
|
26
|
+
@output.puts "#{ @passed_examples.size } examples passed"
|
27
|
+
end
|
28
|
+
@output.flush
|
29
|
+
end
|
30
|
+
|
31
|
+
def dump_failure(*args)
|
32
|
+
lock_output do
|
33
|
+
@output.puts "#{ @failed_examples.size } examples failed:"
|
34
|
+
@failed_examples.each.with_index do | failure, i |
|
35
|
+
@output.puts "#{ i + 1 })"
|
36
|
+
@output.puts failure.header
|
37
|
+
@output.puts failure.exception.to_s
|
38
|
+
failure.exception.backtrace.each do | caller |
|
39
|
+
@output.puts caller
|
40
|
+
end
|
41
|
+
@output.puts ''
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@output.flush
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/parallel_tests.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{parallel_tests}
|
8
|
-
s.version = "0.4.
|
8
|
+
s.version = "0.4.21"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Michael Grosser"]
|
12
|
-
s.date = %q{2011-06-
|
12
|
+
s.date = %q{2011-06-12}
|
13
13
|
s.email = %q{grosser.michael@gmail.com}
|
14
14
|
s.executables = ["parallel_cucumber", "parallel_spec", "parallel_test"]
|
15
15
|
s.files = [
|
@@ -25,7 +25,10 @@ Gem::Specification.new do |s|
|
|
25
25
|
"lib/parallel_cucumber.rb",
|
26
26
|
"lib/parallel_cucumber/runtime_logger.rb",
|
27
27
|
"lib/parallel_specs.rb",
|
28
|
+
"lib/parallel_specs/spec_failures_logger.rb",
|
29
|
+
"lib/parallel_specs/spec_logger_base.rb",
|
28
30
|
"lib/parallel_specs/spec_runtime_logger.rb",
|
31
|
+
"lib/parallel_specs/spec_summary_logger.rb",
|
29
32
|
"lib/parallel_tests.rb",
|
30
33
|
"lib/parallel_tests/grouper.rb",
|
31
34
|
"lib/parallel_tests/railtie.rb",
|
data/spec/parallel_specs_spec.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'spec/spec_helper'
|
2
|
+
require 'parallel_specs/spec_runtime_logger'
|
3
|
+
require 'parallel_specs/spec_summary_logger'
|
4
|
+
require 'parallel_specs/spec_failures_logger'
|
2
5
|
|
3
6
|
describe ParallelSpecs do
|
4
7
|
test_tests_in_groups(ParallelSpecs, 'spec', '_spec.rb')
|
@@ -160,4 +163,103 @@ EOF
|
|
160
163
|
ParallelSpecs.find_results(output).should == ['0 examples, 0 failures, 0 pending','1 examples, 1 failures, 1 pending']
|
161
164
|
end
|
162
165
|
end
|
166
|
+
|
167
|
+
context "logging" do
|
168
|
+
|
169
|
+
OutputLogger = Struct.new(:output) do
|
170
|
+
attr_reader :flock, :flush
|
171
|
+
def puts(s)
|
172
|
+
self.output << s
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
before :each do
|
177
|
+
@output = OutputLogger.new([])
|
178
|
+
@example1 = mock( 'example', :location => '/my/spec/path/to/example:123', :description => 'should do stuff' )
|
179
|
+
@example2 = mock( 'example', :location => '/my/spec/path/to/example2:456', :description => 'should do other stuff' )
|
180
|
+
@exception1 = mock( :to_s => 'exception', :backtrace => [ '/path/to/error/line:33' ] )
|
181
|
+
@failure1 = mock( 'example', :location => '/path/to/example:123', :header => 'header', :exception => @exception1 )
|
182
|
+
end
|
183
|
+
|
184
|
+
describe ParallelSpecs::SpecRuntimeLogger do
|
185
|
+
before :each do
|
186
|
+
ENV['TEST_ENV_NUMBER'] = '1'
|
187
|
+
@logger = ParallelSpecs::SpecRuntimeLogger.new( @output )
|
188
|
+
end
|
189
|
+
|
190
|
+
it "collects runtime information" do
|
191
|
+
@logger.example_started
|
192
|
+
@logger.example_passed( @example1 )
|
193
|
+
|
194
|
+
@logger.start_dump
|
195
|
+
|
196
|
+
@output.output.size.should == 1
|
197
|
+
@output.output[0].size.should == 1
|
198
|
+
@output.output[0][0].should =~ %r(/path/to/example:([\d\.e\-]+))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe ParallelSpecs::SpecSummaryLogger do
|
203
|
+
before :each do
|
204
|
+
@logger = ParallelSpecs::SpecSummaryLogger.new( @output )
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should print a summary of failing examples" do
|
208
|
+
@logger.example_failed( nil, nil, @failure1 )
|
209
|
+
|
210
|
+
@logger.dump_failure
|
211
|
+
|
212
|
+
@output.output.should == ["1 examples failed:", "1)", "header", "exception", "/path/to/error/line:33", ""]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe ParallelSpecs::SpecFailuresLogger do
|
217
|
+
before :each do
|
218
|
+
@logger = ParallelSpecs::SpecFailuresLogger.new( @output )
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should produce a list of command lines for failing examples" do
|
222
|
+
@logger.example_failed( @example1, nil, nil )
|
223
|
+
@logger.example_failed( @example2, nil, nil )
|
224
|
+
|
225
|
+
@logger.dump_failure
|
226
|
+
|
227
|
+
@output.output.size.should == 2
|
228
|
+
@output.output[0].should =~ /r?spec .*? -e "should do stuff"/
|
229
|
+
@output.output[1].should =~ /r?spec .*? -e "should do other stuff"/
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should invoke spec for rspec 1" do
|
233
|
+
ParallelSpecs.stub!(:bundler_enabled?).and_return true
|
234
|
+
ParallelSpecs.stub!(:run).with("bundle show rspec").and_return "/foo/bar/rspec-1.0.2"
|
235
|
+
@logger.example_failed( @example1, nil, nil )
|
236
|
+
|
237
|
+
@logger.dump_failure
|
238
|
+
|
239
|
+
@output.output[0].should =~ /^bundle exec spec/
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should invoke rspec for rspec 2" do
|
243
|
+
ParallelSpecs.stub!(:bundler_enabled?).and_return true
|
244
|
+
ParallelSpecs.stub!(:run).with("bundle show rspec").and_return "/foo/bar/rspec-2.0.2"
|
245
|
+
@logger.example_failed( @example1, nil, nil )
|
246
|
+
|
247
|
+
@logger.dump_failure
|
248
|
+
|
249
|
+
@output.output[0].should =~ /^bundle exec rspec/
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should return relative paths" do
|
253
|
+
@logger.example_failed( @example1, nil, nil )
|
254
|
+
@logger.example_failed( @example2, nil, nil )
|
255
|
+
|
256
|
+
@logger.dump_failure
|
257
|
+
|
258
|
+
@output.output[0].should =~ %r(\./spec/path/to/example)
|
259
|
+
@output.output[1].should =~ %r(\./spec/path/to/example2)
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
163
265
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -54,10 +54,14 @@ def test_tests_in_groups(klass, folder, suffix)
|
|
54
54
|
end
|
55
55
|
|
56
56
|
@log = klass.runtime_log
|
57
|
-
`mkdir #{File.dirname(@log)}`
|
57
|
+
`mkdir -p #{File.dirname(@log)}`
|
58
58
|
`rm -f #{@log}`
|
59
59
|
end
|
60
60
|
|
61
|
+
after :all do
|
62
|
+
`rm -f #{klass.runtime_log}`
|
63
|
+
end
|
64
|
+
|
61
65
|
it "groups when given an array of files" do
|
62
66
|
list_of_files = Dir["#{test_root}/**/*#{suffix}"]
|
63
67
|
found = klass.tests_with_runtime(list_of_files)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parallel_tests
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 37
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 21
|
10
|
+
version: 0.4.21
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Michael Grosser
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-12 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -55,7 +55,10 @@ files:
|
|
55
55
|
- lib/parallel_cucumber.rb
|
56
56
|
- lib/parallel_cucumber/runtime_logger.rb
|
57
57
|
- lib/parallel_specs.rb
|
58
|
+
- lib/parallel_specs/spec_failures_logger.rb
|
59
|
+
- lib/parallel_specs/spec_logger_base.rb
|
58
60
|
- lib/parallel_specs/spec_runtime_logger.rb
|
61
|
+
- lib/parallel_specs/spec_summary_logger.rb
|
59
62
|
- lib/parallel_tests.rb
|
60
63
|
- lib/parallel_tests/grouper.rb
|
61
64
|
- lib/parallel_tests/railtie.rb
|