parallel_tests 0.4.20 → 0.4.21
Sign up to get free protection for your applications and to get access to all the features.
- 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
|