parallel_tests 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  task :default => :spec
2
2
  require 'spec/rake/spectask'
3
- Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color']}
3
+ Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color --backtrace']}
4
4
 
5
5
  begin
6
6
  require 'jeweler'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
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
- lib_folder = File.join(File.dirname(__FILE__), '..', 'lib')
5
- require File.join(lib_folder, "parallel_tests")
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 File.join(lib_folder, "parallel_#{lib}")
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
@@ -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, num, options={})
22
+ def self.tests_in_groups(root, num_groups, options={})
22
23
  if options[:no_sort] == true
23
- distribute_tests_in_groups(root, num)
24
+ Grouper.in_groups(find_tests(root), num_groups)
24
25
  else
25
- sorted_tests_in_groups(root, num)
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.group_size(tests_with_sizes, num_groups)
110
- total_size = tests_with_sizes.inject(0) { |sum, test| sum += test[1] }
111
- total_size / num_groups.to_f
76
+ def self.test_suffix
77
+ "_test.rb"
112
78
  end
113
79
 
114
- def self.find_tests_with_sizes(root)
115
- tests = find_tests(root).sort
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 { |test| [ test, times[test] ] }
129
- else
130
- # use file sizes
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
- require File.join(File.dirname(__FILE__), '..', "parallel_tests")
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
@@ -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.0"
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-05-29}
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",
@@ -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(:slow_specs_first)
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!(:find_tests_with_sizes).and_return([])
60
- ParallelTests.should_receive(:slow_specs_first).and_return([])
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.find_tests_with_sizes(list_of_files)
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
- - 0
9
- version: 0.4.0
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-05-29 00:00:00 +02:00
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