parallel_tests 0.4.0 → 0.4.1
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/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/parallel_test +5 -6
- data/lib/parallel_tests.rb +12 -53
- data/lib/parallel_tests/grouper.rb +31 -0
- data/lib/tasks/parallel_tests.rake +2 -1
- data/parallel_tests.gemspec +3 -2
- data/spec/parallel_tests_spec.rb +3 -9
- data/spec/spec_helper.rb +1 -1
- metadata +4 -3
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.1
|
data/bin/parallel_test
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
3
|
require 'optparse'
|
4
|
-
|
5
|
-
|
4
|
+
require 'parallel'
|
5
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
6
|
+
require "parallel_tests"
|
6
7
|
|
7
8
|
options = {}
|
8
9
|
OptionParser.new do |opts|
|
@@ -26,12 +27,10 @@ end.parse!
|
|
26
27
|
# get files to run from arguments
|
27
28
|
options[:files] = ARGV if ARGV.size > 0
|
28
29
|
|
29
|
-
require 'parallel'
|
30
30
|
num_processes = options[:count] || Parallel.processor_count
|
31
31
|
num_processes = num_processes * (options[:multiply] || 1)
|
32
32
|
|
33
33
|
if options[:execute]
|
34
|
-
require File.join(lib_folder, "parallel_tests")
|
35
34
|
Parallel.in_processes(num_processes) do |i|
|
36
35
|
ParallelTests.execute_command(options[:execute], i)
|
37
36
|
end
|
@@ -42,7 +41,7 @@ else
|
|
42
41
|
'features' => ["cucumber", "feature", "features"]
|
43
42
|
}[options[:type]||'test']
|
44
43
|
|
45
|
-
require
|
44
|
+
require "parallel_#{lib}"
|
46
45
|
klass = eval("Parallel#{lib.capitalize}")
|
47
46
|
|
48
47
|
start = Time.now
|
@@ -77,4 +76,4 @@ else
|
|
77
76
|
# - rake parallel:test && echo 123 ==> 123 should not show up when test failed
|
78
77
|
# - rake parallel:test db:reset ==> works when tests succeed
|
79
78
|
abort "#{name.capitalize}s Failed" if klass.failed?(results)
|
80
|
-
end
|
79
|
+
end
|
data/lib/parallel_tests.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'parallel'
|
2
|
+
require 'parallel_tests/grouper'
|
2
3
|
|
3
4
|
class ParallelTests
|
4
5
|
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
@@ -18,11 +19,11 @@ class ParallelTests
|
|
18
19
|
end
|
19
20
|
|
20
21
|
# finds all tests and partitions them into groups
|
21
|
-
def self.tests_in_groups(root,
|
22
|
+
def self.tests_in_groups(root, num_groups, options={})
|
22
23
|
if options[:no_sort] == true
|
23
|
-
|
24
|
+
Grouper.in_groups(find_tests(root), num_groups)
|
24
25
|
else
|
25
|
-
|
26
|
+
Grouper.in_even_groups_by_size(tests_with_runtime(root), num_groups)
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
@@ -64,40 +65,6 @@ class ParallelTests
|
|
64
65
|
|
65
66
|
protected
|
66
67
|
|
67
|
-
|
68
|
-
def self.sorted_tests_in_groups(root, num)
|
69
|
-
# always add to smallest group
|
70
|
-
groups = Array.new(num){{:tests => [], :size => 0}}
|
71
|
-
tests_with_sizes(root).each do |test, size|
|
72
|
-
smallest = groups.sort_by{|g| g[:size] }.first
|
73
|
-
smallest[:tests] << test
|
74
|
-
smallest[:size] += size
|
75
|
-
end
|
76
|
-
|
77
|
-
groups.map{|g| g[:tests] }
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.distribute_tests_in_groups(root, num)
|
81
|
-
tests = find_tests(root)
|
82
|
-
[].tap do |groups|
|
83
|
-
while ! tests.empty?
|
84
|
-
(0...num).map do |group_number|
|
85
|
-
groups[group_number] ||= []
|
86
|
-
groups[group_number] << tests.shift
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.tests_with_sizes(root)
|
93
|
-
tests_with_sizes = find_tests_with_sizes(root)
|
94
|
-
slow_specs_first(tests_with_sizes)
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.slow_specs_first(tests)
|
98
|
-
tests.sort_by{|test, size| size }.reverse
|
99
|
-
end
|
100
|
-
|
101
68
|
def self.line_is_result?(line)
|
102
69
|
line =~ /\d+ failure/
|
103
70
|
end
|
@@ -106,36 +73,28 @@ class ParallelTests
|
|
106
73
|
line =~ /(\d{2,}|[1-9]) (failure|error)/
|
107
74
|
end
|
108
75
|
|
109
|
-
def self.
|
110
|
-
|
111
|
-
total_size / num_groups.to_f
|
76
|
+
def self.test_suffix
|
77
|
+
"_test.rb"
|
112
78
|
end
|
113
79
|
|
114
|
-
def self.
|
115
|
-
tests = find_tests(root)
|
116
|
-
|
117
|
-
#TODO get the real root, atm this only works for complete runs when root point to e.g. real_root/spec
|
80
|
+
def self.tests_with_runtime(root)
|
81
|
+
tests = find_tests(root)
|
118
82
|
runtime_file = File.join(root,'..','tmp','parallel_profile.log')
|
119
83
|
lines = File.read(runtime_file).split("\n") rescue []
|
120
84
|
|
85
|
+
# use recorded test runtime if we got enough data
|
121
86
|
if lines.size * 1.5 > tests.size
|
122
|
-
# use recorded test runtime if we got enough data
|
123
87
|
times = Hash.new(1)
|
124
88
|
lines.each do |line|
|
125
89
|
test, time = line.split(":")
|
126
90
|
times[test] = time.to_f
|
127
91
|
end
|
128
|
-
tests.map
|
129
|
-
else
|
130
|
-
|
131
|
-
tests.map { |test| [ test, File.stat(test).size ] }
|
92
|
+
tests.sort.map{|test| [test, times[test]] }
|
93
|
+
else # use file sizes
|
94
|
+
tests.sort.map{|test| [test, File.stat(test).size] }
|
132
95
|
end
|
133
96
|
end
|
134
97
|
|
135
|
-
def self.test_suffix
|
136
|
-
"_test.rb"
|
137
|
-
end
|
138
|
-
|
139
98
|
def self.find_tests(root)
|
140
99
|
if root.is_a?(Array)
|
141
100
|
root
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ParallelTests
|
2
|
+
class Grouper
|
3
|
+
def self.in_groups(items, num_groups)
|
4
|
+
[].tap do |groups|
|
5
|
+
while ! items.empty?
|
6
|
+
(0...num_groups).map do |group_number|
|
7
|
+
groups[group_number] ||= []
|
8
|
+
groups[group_number] << items.shift
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.in_even_groups_by_size(items_with_sizes, num_groups)
|
15
|
+
items_with_size = smallest_first(items_with_sizes)
|
16
|
+
groups = Array.new(num_groups){{:items => [], :size => 0}}
|
17
|
+
items_with_size.each do |item, size|
|
18
|
+
# always add to smallest group
|
19
|
+
smallest = groups.sort_by{|g| g[:size] }.first
|
20
|
+
smallest[:items] << item
|
21
|
+
smallest[:size] += size
|
22
|
+
end
|
23
|
+
|
24
|
+
groups.map{|g| g[:items] }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.smallest_first(files)
|
28
|
+
files.sort_by{|item, size| size }.reverse
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -25,7 +25,8 @@ namespace :parallel do
|
|
25
25
|
['test', 'spec', 'features'].each do |type|
|
26
26
|
desc "run #{type} in parallel with parallel:#{type}[num_cpus]"
|
27
27
|
task type, :count, :path_prefix, :options do |t,args|
|
28
|
-
|
28
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..')
|
29
|
+
require "parallel_tests"
|
29
30
|
count, prefix, options = ParallelTests.parse_rake_args(args)
|
30
31
|
exec "#{File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')} --type #{type} -n #{count} -p '#{prefix}' -r '#{RAILS_ROOT}' -o '#{options}'"
|
31
32
|
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.1"
|
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{2010-
|
12
|
+
s.date = %q{2010-06-03}
|
13
13
|
s.email = %q{grosser.michael@gmail.com}
|
14
14
|
s.executables = ["parallel_spec", "parallel_cucumber", "parallel_test"]
|
15
15
|
s.extra_rdoc_files = [
|
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
"lib/parallel_specs.rb",
|
28
28
|
"lib/parallel_specs/spec_runtime_logger.rb",
|
29
29
|
"lib/parallel_tests.rb",
|
30
|
+
"lib/parallel_tests/grouper.rb",
|
30
31
|
"lib/tasks/parallel_tests.rake",
|
31
32
|
"parallel_tests.gemspec",
|
32
33
|
"spec/integration_spec.rb",
|
data/spec/parallel_tests_spec.rb
CHANGED
@@ -51,19 +51,13 @@ describe ParallelTests do
|
|
51
51
|
|
52
52
|
describe :test_in_groups do
|
53
53
|
it "does not sort when passed false do_sort option" do
|
54
|
-
ParallelTests.should_not_receive(:
|
54
|
+
ParallelTests.should_not_receive(:smallest_first)
|
55
55
|
ParallelTests.tests_in_groups [], 1, :no_sort => true
|
56
56
|
end
|
57
57
|
|
58
58
|
it "does sort when not passed do_sort option" do
|
59
|
-
ParallelTests.stub!(:
|
60
|
-
ParallelTests.should_receive(:
|
61
|
-
ParallelTests.tests_in_groups [], 1
|
62
|
-
end
|
63
|
-
|
64
|
-
it "does sort when not passed true do_sort option" do
|
65
|
-
ParallelTests.stub!(:find_tests_with_sizes).and_return([])
|
66
|
-
ParallelTests.should_receive(:slow_specs_first).and_return([])
|
59
|
+
ParallelTests.stub!(:tests_with_runtime).and_return([])
|
60
|
+
ParallelTests::Grouper.should_receive(:smallest_first).and_return([])
|
67
61
|
ParallelTests.tests_in_groups [], 1
|
68
62
|
end
|
69
63
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -32,7 +32,7 @@ def test_tests_in_groups(klass, folder, suffix)
|
|
32
32
|
|
33
33
|
it "groups when given an array of files" do
|
34
34
|
list_of_files = Dir["#{test_root}/**/*#{suffix}"]
|
35
|
-
found = klass.
|
35
|
+
found = klass.tests_with_runtime(list_of_files)
|
36
36
|
found.should =~ list_of_files.map{ |file| [file, File.stat(file).size]}
|
37
37
|
end
|
38
38
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 4
|
8
|
-
-
|
9
|
-
version: 0.4.
|
8
|
+
- 1
|
9
|
+
version: 0.4.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Michael Grosser
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-06-03 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- lib/parallel_specs.rb
|
52
52
|
- lib/parallel_specs/spec_runtime_logger.rb
|
53
53
|
- lib/parallel_tests.rb
|
54
|
+
- lib/parallel_tests/grouper.rb
|
54
55
|
- lib/tasks/parallel_tests.rake
|
55
56
|
- parallel_tests.gemspec
|
56
57
|
- spec/integration_spec.rb
|