parallel_tests 0.8.14 → 0.9.0

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/Gemfile CHANGED
@@ -1,9 +1,8 @@
1
1
  source :rubygems
2
2
  gemspec
3
3
 
4
- group :development do
5
- gem 'test-unit', :platform => :ruby_19
6
- gem 'rspec', '>=2.4'
7
- gem 'cucumber'
8
- gem 'rake'
9
- end
4
+ gem 'bump'
5
+ gem 'test-unit', :platform => :ruby_19
6
+ gem 'rspec', '>=2.4'
7
+ gem 'cucumber'
8
+ gem 'rake'
@@ -1,33 +1,34 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- parallel_tests (0.8.14)
4
+ parallel_tests (0.9.0)
5
5
  parallel
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
9
9
  specs:
10
10
  builder (3.0.0)
11
+ bump (0.3.8)
11
12
  cucumber (1.1.4)
12
13
  builder (>= 2.1.2)
13
14
  diff-lcs (>= 1.1.2)
14
15
  gherkin (~> 2.7.1)
15
16
  json (>= 1.4.6)
16
17
  term-ansicolor (>= 1.0.6)
17
- diff-lcs (1.1.2)
18
+ diff-lcs (1.1.3)
18
19
  gherkin (2.7.6)
19
20
  json (>= 1.4.6)
20
- json (1.6.4)
21
- parallel (0.5.18)
22
- rake (0.8.7)
23
- rspec (2.6.0)
24
- rspec-core (~> 2.6.0)
25
- rspec-expectations (~> 2.6.0)
26
- rspec-mocks (~> 2.6.0)
27
- rspec-core (2.6.4)
28
- rspec-expectations (2.6.0)
29
- diff-lcs (~> 1.1.2)
30
- rspec-mocks (2.6.0)
21
+ json (1.7.5)
22
+ parallel (0.6.1)
23
+ rake (10.0.3)
24
+ rspec (2.12.0)
25
+ rspec-core (~> 2.12.0)
26
+ rspec-expectations (~> 2.12.0)
27
+ rspec-mocks (~> 2.12.0)
28
+ rspec-core (2.12.2)
29
+ rspec-expectations (2.12.1)
30
+ diff-lcs (~> 1.1.3)
31
+ rspec-mocks (2.12.1)
31
32
  term-ansicolor (1.0.7)
32
33
  test-unit (2.4.4)
33
34
 
@@ -35,6 +36,7 @@ PLATFORMS
35
36
  ruby
36
37
 
37
38
  DEPENDENCIES
39
+ bump
38
40
  cucumber
39
41
  parallel_tests!
40
42
  rake
data/Rakefile CHANGED
@@ -1,22 +1,6 @@
1
+ require 'bump/tasks'
1
2
  require 'bundler/gem_tasks'
2
3
 
3
4
  task :default do
4
5
  sh "rspec spec/"
5
6
  end
6
-
7
- # extracted from https://github.com/grosser/project_template
8
- rule /^version:bump:.*/ do |t|
9
- sh "git status | grep 'nothing to commit'" # ensure we are not dirty
10
- index = ['major', 'minor','patch'].index(t.name.split(':').last)
11
- file = 'lib/parallel_tests/version.rb'
12
-
13
- version_file = File.read(file)
14
- old_version, *version_parts = version_file.match(/(\d+)\.(\d+)\.(\d+)/).to_a
15
- version_parts[index] = version_parts[index].to_i + 1
16
- version_parts[2] = 0 if index < 2 # remove patch for minor
17
- version_parts[1] = 0 if index < 1 # remove minor for major
18
- new_version = version_parts * '.'
19
- File.open(file,'w'){|f| f.write(version_file.sub(old_version, new_version)) }
20
-
21
- sh "bundle && git add #{file} Gemfile.lock && git commit -m 'bump version to #{new_version}'"
22
- end
data/Readme.md CHANGED
@@ -132,7 +132,8 @@ Options are:
132
132
  steps - number of cucumber steps
133
133
  default - runtime or filesize
134
134
  -m, --multiply-processes [FLOAT] use given number as a multiplier of processes to run
135
- -s, --single [PATTERN] Run all matching files in only one process
135
+ -s, --single [PATTERN] Run all matching files in the same process
136
+ -i, --isolate Do not run any other tests in the group used by --single(-s)
136
137
  -e, --exec [COMMAND] execute this code parallel and with ENV['TEST_ENV_NUM']
137
138
  -o, --test-options '[OPTIONS]' execute test commands with those options
138
139
  -t, --type [TYPE] test(default) / rspec / cucumber
@@ -176,6 +177,7 @@ TIPS
176
177
 
177
178
  TODO
178
179
  ====
180
+ - fix tests vs cucumber >= 1.2 `unknown option --format`
179
181
  - add tests for the rake tasks, maybe generate a rails project ...
180
182
  - add unit tests for cucumber runtime formatter
181
183
  - make jRuby compatible [basics](http://yehudakatz.com/2009/07/01/new-rails-isolation-testing/)
@@ -222,6 +224,8 @@ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-
222
224
  - [Ulrich Berkmüller](https://github.com/ulrich-berkmueller)
223
225
  - [Grzegorz Derebecki](https://github.com/madmax)
224
226
  - [Florian Motlik](https://github.com/flomotlik)
227
+ - [Artem Kuzko](https://github.com/akuzko)
228
+ - [Zeke Fast](https://github.com/zekefast)
225
229
 
226
230
  [Michael Grosser](http://grosser.it)<br/>
227
231
  michael@grosser.it<br/>
@@ -25,13 +25,13 @@ module ParallelTest
25
25
 
26
26
  report_time_taken do
27
27
  groups = runner.tests_in_groups(options[:files], num_processes, options)
28
- report_number_of_tests runner, groups
28
+ report_number_of_tests(runner, groups)
29
29
 
30
30
  test_results = Parallel.map(groups, :in_processes => groups.size) do |group|
31
31
  run_tests(runner, group, groups.index(group), options)
32
32
  end
33
33
 
34
- report_results runner, test_results
34
+ report_results(runner, test_results)
35
35
  end
36
36
 
37
37
  abort final_fail_message(lib) if any_test_failed?(test_results)
@@ -89,10 +89,20 @@ group tests by:
89
89
  TEXT
90
90
  ) { |type| options[:group_by] = type.to_sym }
91
91
  opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run") { |multiply| options[:multiply] = multiply }
92
- opts.on("-s [PATTERN]", "--single [PATTERN]", "Run all matching files in only one process") do |pattern|
92
+
93
+ opts.on("-s [PATTERN]", "--single [PATTERN]",
94
+ "Run all matching files in the same process") do |pattern|
95
+
93
96
  options[:single_process] ||= []
94
97
  options[:single_process] << /#{pattern}/
95
98
  end
99
+
100
+ opts.on("-i", "--isolate",
101
+ "Do not run any other tests in the group used by --single(-s)") do |pattern|
102
+
103
+ options[:isolate] = true
104
+ end
105
+
96
106
  opts.on("-e", "--exec [COMMAND]", "execute this code parallel and with ENV['TEST_ENV_NUM']") { |path| options[:execute] = path }
97
107
  opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options") { |arg| options[:test_options] = arg }
98
108
  opts.on("-t", "--type [TYPE]", "test(default) / rspec / cucumber") { |type| options[:type] = type }
@@ -131,13 +141,13 @@ TEXT
131
141
  def self.report_time_taken
132
142
  start = Time.now
133
143
  yield
134
- puts ""
135
- puts "Took #{Time.now - start} seconds"
144
+ puts "\nTook #{Time.now - start} seconds"
136
145
  end
137
146
 
138
147
  def self.final_fail_message(lib)
139
148
  fail_message = "#{lib.capitalize}s Failed"
140
149
  fail_message = "\e[31m#{fail_message}\e[0m" if use_colors?
150
+
141
151
  fail_message
142
152
  end
143
153
 
@@ -1,32 +1,19 @@
1
1
  module ParallelTests
2
2
  class Grouper
3
- def self.in_groups(items, num_groups)
4
- groups = Array.new(num_groups){ [] }
5
-
6
- until items.empty?
7
- num_groups.times do |group_number|
8
- if item = items.shift
9
- groups[group_number] << item
10
- end
11
- end
12
- end
13
-
14
- groups.map!(&:sort!)
15
- end
16
-
17
- def self.in_even_groups_by_size(items_with_sizes, num_groups, options={})
18
- groups = Array.new(num_groups){{:items => [], :size => 0}}
3
+ def self.in_even_groups_by_size(items_with_sizes, num_groups, options = {})
4
+ groups = Array.new(num_groups) { {:items => [], :size => 0} }
19
5
 
20
6
  # add all files that should run in a single process to one group
21
- (options[:single_process]||[]).each do |pattern|
22
- matched, items_with_sizes = items_with_sizes.partition{|item, size| item =~ pattern }
23
- smallest = smallest_group(groups)
24
- matched.each{|item,size| add_to_group(smallest, item, size) }
7
+ (options[:single_process] || []).each do |pattern|
8
+ matched, items_with_sizes = items_with_sizes.partition { |item, size| item =~ pattern }
9
+ matched.each { |item, size| add_to_group(groups.first, item, size) }
25
10
  end
26
11
 
12
+ groups_to_fill = (options[:isolate] ? groups[1..-1] : groups)
13
+
27
14
  # add all other files
28
15
  largest_first(items_with_sizes).each do |item, size|
29
- smallest = smallest_group(groups)
16
+ smallest = smallest_group(groups_to_fill)
30
17
  add_to_group(smallest, item, size)
31
18
  end
32
19
 
@@ -108,18 +108,25 @@ namespace :parallel do
108
108
 
109
109
  ['test', 'spec', 'features'].each do |type|
110
110
  desc "run #{type} in parallel with parallel:#{type}[num_cpus]"
111
- task type, [:count, :pattern, :options] do |t,args|
111
+ task type, [:count, :pattern, :options] do |t, args|
112
112
  ParallelTests::Tasks.check_for_pending_migrations
113
+
113
114
  $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
114
115
  require "parallel_tests"
116
+
115
117
  count, pattern, options = ParallelTests::Tasks.parse_args(args)
116
118
  test_framework = {
117
119
  'spec' => 'rspec',
118
120
  'test' => 'test',
119
121
  'features' => 'cucumber'
120
122
  }[type]
123
+
121
124
  executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')
122
- command = "#{executable} #{type} --type #{test_framework} -n #{count} -p '#{pattern}' -o '#{options}'"
125
+ command = "#{executable} #{type} --type #{test_framework} " \
126
+ "-n #{count} " \
127
+ "--pattern '#{pattern}' " \
128
+ "--test-options '#{options}'"
129
+
123
130
  abort unless system(command) # allow to chain tasks e.g. rake parallel:spec parallel:features
124
131
  end
125
132
  end
@@ -31,12 +31,12 @@ module ParallelTests
31
31
  def self.tests_in_groups(tests, num_groups, options={})
32
32
  tests = find_tests(tests, options)
33
33
 
34
- if options[:group_by] == :found
35
- Grouper.in_groups(tests, num_groups)
34
+ tests = if options[:group_by] == :found
35
+ tests.map { |t| [t, 1] }
36
36
  else
37
- tests = with_runtime_info(tests)
38
- Grouper.in_even_groups_by_size(tests, num_groups, options)
37
+ with_runtime_info(tests)
39
38
  end
39
+ Grouper.in_even_groups_by_size(tests, num_groups, options)
40
40
  end
41
41
 
42
42
  def self.execute_command(cmd, process_number, options)
@@ -73,10 +73,11 @@ module ParallelTests
73
73
  sum[word] += number.to_i
74
74
  sum
75
75
  end
76
+
76
77
  sums
77
78
  end
78
79
 
79
- # read output of the process and print in in chucks
80
+ # read output of the process and print it in chunks
80
81
  def self.fetch_output(process)
81
82
  all = ''
82
83
  while buffer = process.readpartial(1000000)
@@ -84,6 +85,7 @@ module ParallelTests
84
85
  $stdout.print buffer
85
86
  $stdout.flush
86
87
  end rescue EOFError
88
+
87
89
  all
88
90
  end
89
91
 
@@ -105,8 +107,8 @@ module ParallelTests
105
107
  end
106
108
  end
107
109
 
108
- def self.find_tests(tests, options={})
109
- (tests||[]).map do |file_or_folder|
110
+ def self.find_tests(tests, options = {})
111
+ (tests || []).map do |file_or_folder|
110
112
  if File.directory?(file_or_folder)
111
113
  files = files_in_folder(file_or_folder, options)
112
114
  files.grep(/#{Regexp.escape test_suffix}$/).grep(options[:pattern]||//)
@@ -1,3 +1,3 @@
1
1
  module ParallelTests
2
- VERSION = Version = '0.8.14'
2
+ VERSION = Version = '0.9.0'
3
3
  end
@@ -49,14 +49,4 @@ describe ParallelTests::Grouper do
49
49
  call(6).should == [["5"], ["4"], ["3"], ["2"], ["1"], []]
50
50
  end
51
51
  end
52
-
53
- describe :in_groups do
54
- it "groups" do
55
- ParallelTests::Grouper.in_groups([1,2,3],2).should == [[1,3],[2]]
56
- end
57
-
58
- it "keeps groups sorted" do
59
- ParallelTests::Grouper.in_groups([3,2,1],2).should == [[1,3],[2]]
60
- end
61
- end
62
52
  end
@@ -24,7 +24,7 @@ describe ParallelTests::RSpec::SummaryLogger do
24
24
  logger.dump_failures
25
25
  output.output.should == []
26
26
  logger.dump_summary(1,2,3,4)
27
- output.output.map{|o| decolorize(o) }.should == ["\nFinished in 1 seconds\n", "2 examples, 3 failures, 4 pending"]
27
+ output.output.map{|o| decolorize(o) }.should == ["\nFinished in 1 second\n", "2 examples, 3 failures, 4 pending"]
28
28
  end
29
29
 
30
30
  it "does not print anything for pending examples" do
@@ -32,6 +32,6 @@ describe ParallelTests::RSpec::SummaryLogger do
32
32
  logger.dump_failures
33
33
  output.output.should == []
34
34
  logger.dump_summary(1,2,3,4)
35
- output.output.map{|o| decolorize(o) }.should == ["\nFinished in 1 seconds\n", "2 examples, 3 failures, 4 pending"]
35
+ output.output.map{|o| decolorize(o) }.should == ["\nFinished in 1 second\n", "2 examples, 3 failures, 4 pending"]
36
36
  end
37
37
  end
@@ -5,7 +5,7 @@ describe ParallelTests::Tasks do
5
5
  describe ".parse_args" do
6
6
  it "should return the count" do
7
7
  args = {:count => 2}
8
- ParallelTests::Tasks.parse_args(args).should == [2, '', ""]
8
+ ParallelTests::Tasks.parse_args(args).should == [2, "", ""]
9
9
  end
10
10
 
11
11
  it "should default to the prefix" do
@@ -19,7 +19,7 @@ describe ParallelTests::Tasks do
19
19
  end
20
20
 
21
21
  it "should return the count, pattern, and options" do
22
- args = {:count => 2, :pattern => "plain", :options => "-p default" }
22
+ args = {:count => 2, :pattern => "plain", :options => "-p default"}
23
23
  ParallelTests::Tasks.parse_args(args).should == [2, "plain", "-p default"]
24
24
  end
25
25
  end
@@ -38,20 +38,41 @@ describe ParallelTests::Test::Runner do
38
38
 
39
39
  it "does not sort when passed false do_sort option" do
40
40
  ParallelTests::Test::Runner.should_not_receive(:smallest_first)
41
- call [], 1, :group_by => :found
41
+ call([], 1, :group_by => :found)
42
42
  end
43
43
 
44
44
  it "does sort when not passed do_sort option" do
45
45
  ParallelTests::Test::Runner.stub!(:tests_with_runtime).and_return([])
46
46
  ParallelTests::Grouper.should_receive(:largest_first).and_return([])
47
- call [], 1
47
+ call([], 1)
48
48
  end
49
49
 
50
50
  it "groups by single_process pattern and then via size" do
51
- ParallelTests::Test::Runner.should_receive(:with_runtime_info).and_return([['aaa',5],['aaa2',5],['bbb',2],['ccc',1],['ddd',1]])
52
- result = call [], 3, :single_process => [/^a.a/]
51
+ ParallelTests::Test::Runner.should_receive(:with_runtime_info).
52
+ and_return([
53
+ ['aaa', 5],
54
+ ['aaa2', 5],
55
+ ['bbb', 2],
56
+ ['ccc', 1],
57
+ ['ddd', 1]
58
+ ])
59
+ result = call([], 3, :single_process => [/^a.a/])
53
60
  result.should == [["aaa", "aaa2"], ["bbb"], ["ccc", "ddd"]]
54
61
  end
62
+
63
+ it "groups by size and adds isolated separately" do
64
+ ParallelTests::Test::Runner.should_receive(:with_runtime_info).
65
+ and_return([
66
+ ['aaa', 0],
67
+ ['bbb', 3],
68
+ ['ccc', 1],
69
+ ['ddd', 2],
70
+ ['eee', 1]
71
+ ])
72
+
73
+ result = call([], 3, :isolate => true, :single_process => [/^aaa/])
74
+ result.should == [["aaa"], ["bbb", "eee"], ["ccc", "ddd"]]
75
+ end
55
76
  end
56
77
 
57
78
  describe :find_results do
@@ -135,7 +135,7 @@ def test_tests_in_groups(klass, folder, suffix)
135
135
  it "partitions by round-robin when not sorting" do
136
136
  files = ["file1.rb", "file2.rb", "file3.rb", "file4.rb"]
137
137
  klass.should_receive(:find_tests).and_return(files)
138
- groups = klass.tests_in_groups(files, 2, :group_by => :found)
138
+ groups = klass.tests_in_groups(files, 2, :group_by => :found).sort
139
139
  groups[0].should == ["file1.rb", "file3.rb"]
140
140
  groups[1].should == ["file2.rb", "file4.rb"]
141
141
  end
@@ -143,7 +143,7 @@ def test_tests_in_groups(klass, folder, suffix)
143
143
  it "alpha-sorts partitions when not sorting by runtime" do
144
144
  files = %w[q w e r t y u i o p a s d f g h j k l z x c v b n m]
145
145
  klass.should_receive(:find_tests).and_return(files)
146
- groups = klass.tests_in_groups(files, 2, :group_by => :found)
146
+ groups = klass.tests_in_groups(files, 2, :group_by => :found).sort
147
147
  groups[0].should == groups[0].sort
148
148
  groups[1].should == groups[1].sort
149
149
  end
metadata CHANGED
@@ -1,32 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.14
5
4
  prerelease:
5
+ version: 0.9.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Michael Grosser
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-18 00:00:00.000000000 Z
12
+ date: 2012-12-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: parallel
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
15
+ version_requirements: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
20
  none: false
21
+ prerelease: false
22
+ name: parallel
23
+ requirement: !ruby/object:Gem::Requirement
26
24
  requirements:
27
25
  - - ! '>='
28
26
  - !ruby/object:Gem::Version
29
27
  version: '0'
28
+ none: false
29
+ type: :runtime
30
30
  description:
31
31
  email: michael@grosser.it
32
32
  executables:
@@ -84,23 +84,23 @@ rdoc_options: []
84
84
  require_paths:
85
85
  - lib
86
86
  required_ruby_version: !ruby/object:Gem::Requirement
87
- none: false
88
87
  requirements:
89
88
  - - ! '>='
90
89
  - !ruby/object:Gem::Version
91
90
  version: '0'
92
91
  segments:
93
92
  - 0
94
- hash: 1255476358488583165
95
- required_rubygems_version: !ruby/object:Gem::Requirement
93
+ hash: -4090660521181020432
96
94
  none: false
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
96
  requirements:
98
97
  - - ! '>='
99
98
  - !ruby/object:Gem::Version
100
99
  version: '0'
101
100
  segments:
102
101
  - 0
103
- hash: 1255476358488583165
102
+ hash: -4090660521181020432
103
+ none: false
104
104
  requirements: []
105
105
  rubyforge_project:
106
106
  rubygems_version: 1.8.24