parallel_tests 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.md +4 -5
- data/VERSION +1 -1
- data/bin/parallel_test +3 -3
- data/lib/parallel_tests.rb +27 -18
- data/lib/parallel_tests/tasks.rb +7 -7
- data/parallel_tests.gemspec +2 -2
- data/spec/integration_spec.rb +6 -9
- data/spec/parallel_tests_spec.rb +52 -4
- data/spec/spec_helper.rb +1 -1
- metadata +4 -4
data/Readme.md
CHANGED
@@ -61,13 +61,12 @@ OR as plugin
|
|
61
61
|
rake parallel:test --> got 4 CPUs? --> 26 seconds
|
62
62
|
...
|
63
63
|
|
64
|
-
Test
|
64
|
+
Test by pattern (e.g. use one integration server per subfolder / see if you broke any user-related tests)
|
65
65
|
|
66
|
-
rake parallel:test[
|
67
|
-
rake parallel:test[
|
66
|
+
rake parallel:test[^unit] # everything in test/unit folder (every test file matching /^unit/)
|
67
|
+
rake parallel:test[user] # run users_controller + user_helper + user tests
|
68
|
+
rake parallel:test['user|product'] # run user and product related tests
|
68
69
|
|
69
|
-
partial paths are OK too...
|
70
|
-
rake parallel:test[functional] == rake parallel:test[fun]
|
71
70
|
|
72
71
|
Example output
|
73
72
|
--------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/bin/parallel_test
CHANGED
@@ -17,7 +17,7 @@ Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2',
|
|
17
17
|
Options are:
|
18
18
|
BANNER
|
19
19
|
opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs"){|n| options[:count] = n }
|
20
|
-
opts.on("-p", '--
|
20
|
+
opts.on("-p", '--pattern [PATTERN]', "run tests matching this pattern"){|pattern| options[:pattern] = pattern }
|
21
21
|
opts.on("--no-sort", "do not sort files before running them"){ |no_sort| options[:no_sort] = no_sort }
|
22
22
|
opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run"){ |multiply| options[:multiply] = multiply }
|
23
23
|
opts.on("-r", '--root [PATH]', "execute test commands from this path"){|path| options[:root] = path }
|
@@ -60,10 +60,10 @@ else
|
|
60
60
|
|
61
61
|
start = Time.now
|
62
62
|
|
63
|
-
tests_folder =
|
63
|
+
tests_folder = task
|
64
64
|
tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
|
65
65
|
|
66
|
-
groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, :no_sort => options[:no_sort])
|
66
|
+
groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, :no_sort => options[:no_sort], :pattern => options[:pattern])
|
67
67
|
num_processes = groups.size
|
68
68
|
|
69
69
|
#adjust processes to groups
|
data/lib/parallel_tests.rb
CHANGED
@@ -5,26 +5,31 @@ require 'parallel_tests/railtie'
|
|
5
5
|
class ParallelTests
|
6
6
|
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
7
7
|
|
8
|
-
# parallel:spec[
|
8
|
+
# parallel:spec[:count, :pattern, :options]
|
9
9
|
def self.parse_rake_args(args)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
# order as given by user
|
11
|
+
args = [args[:count], args[:pattern], args[:options]]
|
12
|
+
|
13
|
+
# count given or empty ?
|
14
|
+
# parallel:spec[2,models,options]
|
15
|
+
# parallel:spec[,models,options]
|
16
|
+
count = args.shift if args.first.to_s =~ /^\d*$/
|
17
|
+
num_processes = (count.to_s.empty? ? Parallel.processor_count : count.to_i)
|
18
|
+
|
19
|
+
pattern = args.shift
|
20
|
+
options = args.shift
|
21
|
+
|
22
|
+
[num_processes.to_i, pattern.to_s, options.to_s]
|
20
23
|
end
|
21
24
|
|
22
25
|
# finds all tests and partitions them into groups
|
23
26
|
def self.tests_in_groups(root, num_groups, options={})
|
27
|
+
tests = find_tests(root, options)
|
24
28
|
if options[:no_sort] == true
|
25
|
-
Grouper.in_groups(
|
29
|
+
Grouper.in_groups(tests, num_groups)
|
26
30
|
else
|
27
|
-
|
31
|
+
tests = with_runtime_info(tests)
|
32
|
+
Grouper.in_even_groups_by_size(tests, num_groups)
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
@@ -91,7 +96,7 @@ class ParallelTests
|
|
91
96
|
|
92
97
|
# copied from http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
|
93
98
|
def self.bundler_enabled?
|
94
|
-
return true if Object.const_defined?(:Bundler)
|
99
|
+
return true if Object.const_defined?(:Bundler)
|
95
100
|
|
96
101
|
previous = nil
|
97
102
|
current = File.expand_path(Dir.pwd)
|
@@ -113,8 +118,7 @@ class ParallelTests
|
|
113
118
|
"_test.rb"
|
114
119
|
end
|
115
120
|
|
116
|
-
def self.
|
117
|
-
tests = find_tests(root)
|
121
|
+
def self.with_runtime_info(tests)
|
118
122
|
lines = File.read(runtime_log).split("\n") rescue []
|
119
123
|
|
120
124
|
# use recorded test runtime if we got enough data
|
@@ -131,11 +135,16 @@ class ParallelTests
|
|
131
135
|
end
|
132
136
|
end
|
133
137
|
|
134
|
-
def self.find_tests(root)
|
138
|
+
def self.find_tests(root, options={})
|
135
139
|
if root.is_a?(Array)
|
136
140
|
root
|
137
141
|
else
|
138
|
-
|
142
|
+
# follow one symlink and direct children
|
143
|
+
# http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
|
144
|
+
files = Dir["#{root}/**{,/*/**}/*#{test_suffix}"].uniq
|
145
|
+
files = files.map{|f| f.sub(root+'/','') }
|
146
|
+
files = files.grep(/#{options[:pattern]}/)
|
147
|
+
files.map{|f| "#{root}/#{f}" }
|
139
148
|
end
|
140
149
|
end
|
141
150
|
end
|
data/lib/parallel_tests/tasks.rb
CHANGED
@@ -43,12 +43,12 @@ namespace :parallel do
|
|
43
43
|
|
44
44
|
['test', 'spec', 'features'].each do |type|
|
45
45
|
desc "run #{type} in parallel with parallel:#{type}[num_cpus]"
|
46
|
-
task type, :count, :
|
46
|
+
task type, :count, :pattern, :options do |t,args|
|
47
47
|
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
48
48
|
require "parallel_tests"
|
49
|
-
count,
|
49
|
+
count, pattern, options = ParallelTests.parse_rake_args(args)
|
50
50
|
executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')
|
51
|
-
command = "#{executable} --type #{type} -n #{count} -p '#{
|
51
|
+
command = "#{executable} --type #{type} -n #{count} -p '#{pattern}' -r '#{Rails.root}' -o '#{options}'"
|
52
52
|
abort unless system(command) # allow to chain tasks e.g. rake parallel:spec parallel:features
|
53
53
|
end
|
54
54
|
end
|
@@ -66,15 +66,15 @@ namespace :spec do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
task :parallel, :count, :
|
69
|
+
task :parallel, :count, :pattern do |t,args|
|
70
70
|
$stderr.puts "WARNING -- Deprecated! use parallel:spec"
|
71
|
-
Rake::Task['parallel:spec'].invoke(args[:count], args[:
|
71
|
+
Rake::Task['parallel:spec'].invoke(args[:count], args[:pattern])
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
75
|
namespace :test do
|
76
|
-
task :parallel, :count, :
|
76
|
+
task :parallel, :count, :pattern do |t,args|
|
77
77
|
$stderr.puts "WARNING -- Deprecated! use parallel:test"
|
78
|
-
Rake::Task['parallel:test'].invoke(args[:count], args[:
|
78
|
+
Rake::Task['parallel:test'].invoke(args[:count], args[:pattern])
|
79
79
|
end
|
80
80
|
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.
|
8
|
+
s.version = "0.6.0"
|
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-
|
12
|
+
s.date = %q{2011-08-09}
|
13
13
|
s.email = %q{grosser.michael@gmail.com}
|
14
14
|
s.executables = ["parallel_cucumber", "parallel_test", "parallel_spec"]
|
15
15
|
s.files = [
|
data/spec/integration_spec.rb
CHANGED
@@ -59,8 +59,8 @@ describe 'CLI' do
|
|
59
59
|
end
|
60
60
|
|
61
61
|
it "can exec given commands with ENV['TEST_ENV_NUM']" do
|
62
|
-
result = `#{executable} -e 'ruby -e "
|
63
|
-
result.
|
62
|
+
result = `#{executable} -e 'ruby -e "print ENV[:TEST_ENV_NUMBER.to_s].to_i"' -n 4`
|
63
|
+
result.gsub('"','').split('').sort.should == %w[0 2 3 4]
|
64
64
|
end
|
65
65
|
|
66
66
|
it "can exec given command non-parallel" do
|
@@ -83,14 +83,11 @@ describe 'CLI' do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
it "runs faster with more processes" do
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
write 'xxx4_spec.rb', 'describe("it"){it("should"){sleep 2}}'
|
90
|
-
write 'xxx5_spec.rb', 'describe("it"){it("should"){sleep 2}}'
|
91
|
-
write 'xxx6_spec.rb', 'describe("it"){it("should"){sleep 2}}'
|
86
|
+
2.times{|i|
|
87
|
+
write "xxx#{i}_spec.rb", 'describe("it"){it("should"){sleep 5}}; $stderr.puts ENV["TEST_ENV_NUMBER"]'
|
88
|
+
}
|
92
89
|
t = Time.now
|
93
|
-
run_specs
|
90
|
+
puts run_specs(:processes => 2)
|
94
91
|
expected = 10
|
95
92
|
(Time.now - t).should <= expected
|
96
93
|
end
|
data/spec/parallel_tests_spec.rb
CHANGED
@@ -14,13 +14,13 @@ describe ParallelTests do
|
|
14
14
|
ParallelTests.parse_rake_args(args).should == [Parallel.processor_count, "models", ""]
|
15
15
|
end
|
16
16
|
|
17
|
-
it "should return the count and
|
18
|
-
args = {:count => 2, :
|
17
|
+
it "should return the count and pattern" do
|
18
|
+
args = {:count => 2, :pattern => "models"}
|
19
19
|
ParallelTests.parse_rake_args(args).should == [2, "models", ""]
|
20
20
|
end
|
21
21
|
|
22
|
-
it "should return the count,
|
23
|
-
args = {:count => 2, :
|
22
|
+
it "should return the count, pattern, and options" do
|
23
|
+
args = {:count => 2, :pattern => "plain", :options => "-p default" }
|
24
24
|
ParallelTests.parse_rake_args(args).should == [2, "plain", "-p default"]
|
25
25
|
end
|
26
26
|
end
|
@@ -136,6 +136,54 @@ EOF
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
+
describe :find_tests do
|
140
|
+
it "returns if root is an array" do
|
141
|
+
ParallelTests.send(:find_tests, [1]).should == [1]
|
142
|
+
end
|
143
|
+
|
144
|
+
it "finds all test files" do
|
145
|
+
begin
|
146
|
+
root = "/tmp/test-find_tests-#{rand(999)}"
|
147
|
+
`mkdir #{root}`
|
148
|
+
`mkdir #{root}/a`
|
149
|
+
`mkdir #{root}/b`
|
150
|
+
`touch #{root}/x_test.rb`
|
151
|
+
`touch #{root}/a/x_test.rb`
|
152
|
+
`touch #{root}/a/test.rb`
|
153
|
+
`touch #{root}/b/y_test.rb`
|
154
|
+
`touch #{root}/b/test.rb`
|
155
|
+
`ln -s #{root}/b #{root}/c`
|
156
|
+
`ln -s #{root}/b #{root}/a/`
|
157
|
+
ParallelTests.send(:find_tests, root).sort.should == [
|
158
|
+
"#{root}/a/b/y_test.rb",
|
159
|
+
"#{root}/a/x_test.rb",
|
160
|
+
"#{root}/b/y_test.rb",
|
161
|
+
"#{root}/c/y_test.rb",
|
162
|
+
"#{root}/x_test.rb"
|
163
|
+
]
|
164
|
+
ensure
|
165
|
+
`rm -rf #{root}`
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
it "finds files by pattern" do
|
170
|
+
begin
|
171
|
+
root = "/tmp/test-find_tests-#{rand(999)}"
|
172
|
+
`mkdir #{root}`
|
173
|
+
`mkdir #{root}/a`
|
174
|
+
`touch #{root}/a/x_test.rb`
|
175
|
+
`touch #{root}/a/y_test.rb`
|
176
|
+
`touch #{root}/a/z_test.rb`
|
177
|
+
ParallelTests.send(:find_tests, root, :pattern => '^a/(y|z)_test').sort.should == [
|
178
|
+
"#{root}/a/y_test.rb",
|
179
|
+
"#{root}/a/z_test.rb",
|
180
|
+
]
|
181
|
+
ensure
|
182
|
+
`rm -rf #{root}`
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
139
187
|
it "has a version" do
|
140
188
|
ParallelTests::VERSION.should =~ /^\d+\.\d+\.\d+$/
|
141
189
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -64,7 +64,7 @@ def test_tests_in_groups(klass, folder, suffix)
|
|
64
64
|
|
65
65
|
it "groups when given an array of files" do
|
66
66
|
list_of_files = Dir["#{test_root}/**/*#{suffix}"]
|
67
|
-
found = klass.
|
67
|
+
found = klass.with_runtime_info(list_of_files)
|
68
68
|
found.should =~ list_of_files.map{ |file| [file, File.stat(file).size]}
|
69
69
|
end
|
70
70
|
|
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: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 6
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.6.0
|
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-
|
18
|
+
date: 2011-08-09 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|