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 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