parallel_tests 0.6.7 → 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
data/Readme.md CHANGED
@@ -1,5 +1,5 @@
1
1
  Speedup Test::Unit + RSpec + Cucumber by running parallel on multiple CPUs (or cores).<br/>
2
- ParallelTests splits tests into even groups and runs each group in a single process with its own database.
2
+ ParallelTests splits tests into even groups(by number of tests or runtime) and runs each group in a single process with its own database.
3
3
 
4
4
  Setup for Rails
5
5
  ===============
@@ -87,7 +87,7 @@ Spec Loggers
87
87
  Even process runtimes
88
88
  -----------------
89
89
 
90
- Log test runtime to give each process the same test runtime.
90
+ Log test runtime to give each process the same runtime.
91
91
 
92
92
  Add to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
93
93
 
@@ -102,7 +102,7 @@ Add to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
102
102
  SpecSummaryLogger
103
103
  --------------------
104
104
 
105
- This logger stops the different processes overwriting each other's output.
105
+ This logger logs the test output without the different processes overwriting each other.
106
106
 
107
107
  Add the following to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
108
108
 
@@ -116,7 +116,7 @@ Add the following to your `spec/parallel_spec.opts` (or `spec/spec.opts`) :
116
116
  SpecFailuresLogger
117
117
  -----------------------
118
118
 
119
- This logger produces command lines for running any failing examples.
119
+ This logger produces pasteable command-line snippets for each failed example.
120
120
 
121
121
  E.g.
122
122
 
@@ -177,6 +177,7 @@ TIPS
177
177
  - [RSpec] if `script/spec` is missing parallel:spec uses just `spec` (which solves some issues with double-loaded environment.rb)
178
178
  - [RSpec] 'script/spec_server' or [spork](http://github.com/timcharper/spork/tree/master) do not work in parallel
179
179
  - [RSpec] `./script/generate rspec` if you are running rspec from gems (this plugin uses script/spec which may fail if rspec files are outdated)
180
+ - [RSpec] remove --loadby from you spec/*.opts
180
181
  - [Bundler] if you have a `Gemfile` then `bundle exec` will be used to run tests
181
182
  - [Capybara setup](https://github.com/grosser/parallel_tests/wiki)
182
183
  - [Sphinx setup](https://github.com/grosser/parallel_tests/wiki)
@@ -219,4 +220,5 @@ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-
219
220
 
220
221
  [Michael Grosser](http://grosser.it)<br/>
221
222
  michael@grosser.it<br/>
222
- Hereby placed under public domain, do what you want, just do not hold me accountable...
223
+ Hereby placed under public domain, do what you want, just do not hold me accountable...<br/>
224
+ [![Flattr](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=grosser&url=https://github.com/grosser/parallel_tests&title=parallel_tests&language=en_GB&tags=github&category=software)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.7
1
+ 0.6.8
data/bin/parallel_test CHANGED
@@ -21,6 +21,10 @@ BANNER
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 }
24
+ opts.on("-s [PATTERN]", "--single [PATTERN]", "Run all matching files in only one process") do |pattern|
25
+ options[:single_process] ||= []
26
+ options[:single_process] << /#{pattern}/
27
+ end
24
28
  opts.on("-e", '--exec [COMMAND]', "execute this code parallel and with ENV['TEST_ENV_NUM']"){|path| options[:execute] = path }
25
29
  opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options"){|arg| options[:test_options] = arg }
26
30
  opts.on("-t", "--type [TYPE]", "which type of tests to run? test, spec or features"){|type| options[:type] = type }
@@ -30,6 +34,8 @@ BANNER
30
34
  opts.on("-h", "--help", "Show this.") { puts opts; exit }
31
35
  end.parse!
32
36
 
37
+ raise "--no-sort and --single-process are not supported" if options[:no_sort] and options[:single_process]
38
+
33
39
  # get files to run from arguments
34
40
  options[:files] = ARGV if ARGV.size > 0
35
41
 
@@ -63,7 +69,7 @@ else
63
69
  tests_folder = task
64
70
  tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
65
71
 
66
- groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, :no_sort => options[:no_sort], :pattern => options[:pattern])
72
+ groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, options)
67
73
  num_processes = groups.size
68
74
 
69
75
  #adjust processes to groups
@@ -1,31 +1,50 @@
1
1
  class ParallelTests
2
2
  class Grouper
3
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
4
+ groups = Array.new(num_groups){ [] }
5
+
6
+ until items.empty?
7
+ num_groups.times do |group_number|
8
+ groups[group_number] << items.shift
10
9
  end
11
- end.map(&:sort)
10
+ end
11
+
12
+ groups.map!(&:sort!)
12
13
  end
13
14
 
14
- def self.in_even_groups_by_size(items_with_sizes, num_groups)
15
- items_with_size = smallest_first(items_with_sizes)
15
+ def self.in_even_groups_by_size(items_with_sizes, num_groups, options={})
16
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
17
+
18
+ # add all files that should run in a single process to one group
19
+ (options[:single_process]||[]).each do |pattern|
20
+ matched, items_with_sizes = items_with_sizes.partition{|item, size| item =~ pattern }
21
+ puts matched.inspect
22
+ smallest = smallest_group(groups)
23
+ matched.each{|item,size| add_to_group(smallest, item, size) }
24
+ end
25
+
26
+ # add all other files
27
+ smallest_first(items_with_sizes).each do |item, size|
28
+ smallest = smallest_group(groups)
29
+ add_to_group(smallest, item, size)
22
30
  end
23
31
 
24
- groups.map{|g| g[:items].sort }
32
+ groups.map!{|g| g[:items].sort }
25
33
  end
26
34
 
27
35
  def self.smallest_first(files)
28
36
  files.sort_by{|item, size| size }.reverse
29
37
  end
38
+
39
+ private
40
+
41
+ def self.smallest_group(groups)
42
+ groups.min_by{|g| g[:size] }
43
+ end
44
+
45
+ def self.add_to_group(group, item, size)
46
+ group[:items] << item
47
+ group[:size] += size
48
+ end
30
49
  end
31
50
  end
@@ -31,13 +31,12 @@ class ParallelTests
31
31
  Grouper.in_groups(tests, num_groups)
32
32
  else
33
33
  tests = with_runtime_info(tests)
34
- Grouper.in_even_groups_by_size(tests, num_groups)
34
+ Grouper.in_even_groups_by_size(tests, num_groups, options)
35
35
  end
36
36
  end
37
37
 
38
38
  def self.run_tests(test_files, process_number, options)
39
39
  require_list = test_files.map { |filename| %{"#{File.expand_path filename}"} }.join(",")
40
- test_options = "- #{options[:test_options]}" unless RUBY_VERSION > '1.9.2' # test options passing is broken on 1.9.3 see issue #64
41
40
  cmd = "ruby -Itest -e '[#{require_list}].each {|f| require f }' -- #{options[:test_options]}"
42
41
  execute_command(cmd, process_number, options)
43
42
  end
@@ -4,14 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{parallel_tests}
8
- s.version = "0.6.7"
7
+ s.name = "parallel_tests"
8
+ s.version = "0.6.8"
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-10-05}
13
- s.email = %q{grosser.michael@gmail.com}
14
- s.executables = ["parallel_cucumber", "parallel_test", "parallel_spec"]
12
+ s.date = "2011-11-02"
13
+ s.email = "grosser.michael@gmail.com"
14
+ s.executables = ["parallel_cucumber", "parallel_spec", "parallel_test"]
15
15
  s.files = [
16
16
  "Gemfile",
17
17
  "Gemfile.lock",
@@ -40,10 +40,10 @@ Gem::Specification.new do |s|
40
40
  "spec/parallel_tests_spec.rb",
41
41
  "spec/spec_helper.rb"
42
42
  ]
43
- s.homepage = %q{http://github.com/grosser/parallel_tests}
43
+ s.homepage = "http://github.com/grosser/parallel_tests"
44
44
  s.require_paths = ["lib"]
45
- s.rubygems_version = %q{1.6.2}
46
- s.summary = %q{Run tests / specs / features in parallel}
45
+ s.rubygems_version = "1.8.10"
46
+ s.summary = "Run tests / specs / features in parallel"
47
47
 
48
48
  if s.respond_to? :specification_version then
49
49
  s.specification_version = 3
@@ -49,6 +49,13 @@ describe 'CLI' do
49
49
  $?.success?.should == true
50
50
  end
51
51
 
52
+ it "does not run any tests if there are none" do
53
+ write 'spec/xxx.rb', 'xxx'
54
+ result = run_tests
55
+ result.should include('No examples found')
56
+ result.should include('Took')
57
+ end
58
+
52
59
  it "fails when tests fail" do
53
60
  write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
54
61
  write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){1.should == 2}}'
@@ -71,6 +71,12 @@ describe ParallelTests do
71
71
  ParallelTests::Grouper.should_receive(:smallest_first).and_return([])
72
72
  ParallelTests.tests_in_groups [], 1
73
73
  end
74
+
75
+ it "groups by single_process pattern and then via size" do
76
+ ParallelTests.should_receive(:with_runtime_info).and_return([['aaa',5],['aaa2',5],['bbb',2],['ccc',1],['ddd',1]])
77
+ result = ParallelTests.tests_in_groups [], 3, :single_process => [/^a.a/]
78
+ result.should == [["aaa", "aaa2"], ["bbb"], ["ccc", "ddd"]]
79
+ end
74
80
  end
75
81
 
76
82
  describe :find_results do
@@ -94,7 +100,7 @@ EOF
94
100
  ParallelTests.find_results(output).should == ['10 tests, 20 assertions, 0 failures, 0 errors','14 tests, 20 assertions, 0 failures, 0 errors']
95
101
  end
96
102
 
97
- it "is robust against scrambeled output" do
103
+ it "is robust against scrambled output" do
98
104
  output = <<EOF
99
105
  Loaded suite /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
100
106
  Started
metadata CHANGED
@@ -1,48 +1,36 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
- version: !ruby/object:Gem::Version
4
- hash: 9
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.8
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 6
9
- - 7
10
- version: 0.6.7
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Michael Grosser
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-10-05 00:00:00 +02:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
22
- requirement: &id001 !ruby/object:Gem::Requirement
12
+ date: 2011-11-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: parallel
16
+ requirement: &76244280 !ruby/object:Gem::Requirement
23
17
  none: false
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- hash: 3
28
- segments:
29
- - 0
30
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
31
22
  type: :runtime
32
- name: parallel
33
- version_requirements: *id001
34
23
  prerelease: false
24
+ version_requirements: *76244280
35
25
  description:
36
26
  email: grosser.michael@gmail.com
37
- executables:
27
+ executables:
38
28
  - parallel_cucumber
39
- - parallel_test
40
29
  - parallel_spec
30
+ - parallel_test
41
31
  extensions: []
42
-
43
32
  extra_rdoc_files: []
44
-
45
- files:
33
+ files:
46
34
  - Gemfile
47
35
  - Gemfile.lock
48
36
  - Rakefile
@@ -69,39 +57,31 @@ files:
69
57
  - spec/parallel_specs_spec.rb
70
58
  - spec/parallel_tests_spec.rb
71
59
  - spec/spec_helper.rb
72
- has_rdoc: true
73
60
  homepage: http://github.com/grosser/parallel_tests
74
61
  licenses: []
75
-
76
62
  post_install_message:
77
63
  rdoc_options: []
78
-
79
- require_paths:
64
+ require_paths:
80
65
  - lib
81
- required_ruby_version: !ruby/object:Gem::Requirement
66
+ required_ruby_version: !ruby/object:Gem::Requirement
82
67
  none: false
83
- requirements:
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- hash: 3
87
- segments:
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ segments:
88
73
  - 0
89
- version: "0"
90
- required_rubygems_version: !ruby/object:Gem::Requirement
74
+ hash: -400446063
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
76
  none: false
92
- requirements:
93
- - - ">="
94
- - !ruby/object:Gem::Version
95
- hash: 3
96
- segments:
97
- - 0
98
- version: "0"
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
99
81
  requirements: []
100
-
101
82
  rubyforge_project:
102
- rubygems_version: 1.6.2
83
+ rubygems_version: 1.8.10
103
84
  signing_key:
104
85
  specification_version: 3
105
86
  summary: Run tests / specs / features in parallel
106
87
  test_files: []
107
-