parallel_tests 0.7.0.alpha2 → 0.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- parallel_tests (0.7.0.alpha2)
4
+ parallel_tests (0.7.0.rc1)
5
5
  parallel
6
6
 
7
7
  GEM
@@ -18,7 +18,7 @@ GEM
18
18
  gherkin (2.7.6)
19
19
  json (>= 1.4.6)
20
20
  json (1.6.4)
21
- parallel (0.5.1)
21
+ parallel (0.5.2)
22
22
  rake (0.8.7)
23
23
  rspec (2.6.0)
24
24
  rspec-core (~> 2.6.0)
data/Readme.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Speedup Test::Unit + RSpec + Cucumber by running parallel on multiple CPUs (or cores).<br/>
2
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
+ [upgrading from 0.6 ?](https://github.com/grosser/parallel_tests/wiki/Upgrading-0.6.x-to-0.7.x)
5
+
4
6
  Setup for Rails
5
7
  ===============
6
8
  [still using Rails 2?](https://github.com/grosser/parallel_tests/blob/master/ReadmeRails2.md)
@@ -46,7 +48,7 @@ ParallelTests uses 1 database per test-process, 2 processes will use `*_test` an
46
48
 
47
49
  Test by pattern (e.g. use one integration server per subfolder / see if you broke any 'user'-related tests)
48
50
 
49
- rake parallel:test[^unit] # everything in test/unit folder (every test file matching /^unit/)
51
+ rake parallel:test[^test/unit] # every test file in test/unit folder
50
52
  rake parallel:test[user] # run users_controller + user_helper + user tests
51
53
  rake parallel:test['user|product'] # run user and product related tests
52
54
 
@@ -73,7 +75,7 @@ Rspec: Add to your `.rspec_parallel` (or `.rspec`) :
73
75
  RSpec
74
76
  If installed as plugin: -I vendor/plugins/parallel_tests/lib
75
77
  --format progress
76
- --format ParallelTests::Spec::RuntimeLogger --out tmp/parallel_profile.log
78
+ --format ParallelTests::Spec::RuntimeLogger --out tmp/parallel_runtime_spec.log
77
79
 
78
80
  Test::Unit: Add to your `test_helper.rb`:
79
81
 
@@ -168,7 +170,6 @@ TIPS
168
170
  TODO
169
171
  ====
170
172
  - document how to use cucumber runtime logger
171
- - unify runtime-log location
172
173
  - add tests for cucumber runtime formatter
173
174
  - make jRuby compatible [basics](http://yehudakatz.com/2009/07/01/new-rails-isolation-testing/)
174
175
  - make windows compatible
data/ReadmeRails2.md CHANGED
@@ -19,13 +19,17 @@ OR as plugin
19
19
 
20
20
  ./script/plugin install git://github.com/grosser/parallel_tests.git
21
21
 
22
+ # add to Rakefile
23
+ begin; require 'vendor/plugins/parallel_tests/lib/parallel_tests/tasks'; rescue LoadError; end
24
+
25
+
22
26
  Even process runtimes
23
27
  -----------------
24
28
 
25
29
  RSpec 1.x:
26
30
  --format progress
27
31
  --require parallel_tests/spec/runtime_logger
28
- --format ParallelTests::Spec::RuntimeLogger:tmp/parallel_profile.log
32
+ --format ParallelTests::Spec::RuntimeLogger:tmp/parallel_runtime_spec.log
29
33
 
30
34
  SpecSummaryLogger
31
35
  --------------------
@@ -1,6 +1,7 @@
1
1
  require 'parallel'
2
2
  require 'parallel_tests/version'
3
3
  require 'parallel_tests/grouper'
4
+ require 'parallel_tests/railtie' if defined? Rails::Railtie
4
5
 
5
6
  module ParallelTests
6
7
  def self.determine_number_of_processes(count)
@@ -5,7 +5,6 @@ module ParallelTest
5
5
  module CLI
6
6
  def self.run(argv)
7
7
  options = parse_options!(argv)
8
- test_results = nil
9
8
 
10
9
  num_processes = ParallelTests.determine_number_of_processes(options[:count])
11
10
  num_processes = num_processes * (options[:multiply] || 1)
@@ -13,40 +12,61 @@ module ParallelTest
13
12
  if options[:execute]
14
13
  execute_shell_command_in_parallel(options[:execute], num_processes, options)
15
14
  else
16
- lib = options[:type] || 'test'
17
- require "parallel_tests/#{lib}/runner"
18
- runner = eval("ParallelTests::#{lib.capitalize}::Runner")
19
- name = runner.test_file_name
20
-
21
- report_time_taken do
22
- groups = runner.tests_in_groups(options[:files], num_processes, options)
23
- abort "no #{name}s found!" if groups.size == 0
24
-
25
- num_processes = groups.size
26
- num_tests = groups.inject(0) { |sum, item| sum + item.size }
27
- puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
28
-
29
- test_results = Parallel.map(groups, :in_processes => num_processes) do |group|
30
- if group.empty?
31
- {:stdout => '', :exit_status => 0}
32
- else
33
- runner.run_tests(group, groups.index(group), options)
34
- end
35
- end
36
-
37
- #parse and print results
38
- results = runner.find_results(test_results.map { |result| result[:stdout] }*"")
39
- puts ""
40
- puts runner.summarize_results(results)
15
+ run_tests_in_parallel(num_processes, options)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def self.run_tests_in_parallel(num_processes, options)
22
+ test_results = nil
23
+ lib = options[:type] || 'test'
24
+ runner = load_runner_for(lib)
25
+
26
+ report_time_taken do
27
+ groups = runner.tests_in_groups(options[:files], num_processes, options)
28
+ report_number_of_tests runner, groups
29
+
30
+ test_results = Parallel.map(groups, :in_processes => groups.size) do |group|
31
+ run_tests(runner, group, groups.index(group), options)
41
32
  end
42
33
 
43
- #exit with correct status code so rake parallel:test && echo 123 works
44
- failed = test_results.any? { |result| result[:exit_status] != 0 }
45
- abort "#{lib.capitalize}s Failed" if failed
34
+ report_results runner, test_results
46
35
  end
36
+
37
+ abort "#{lib.capitalize}s Failed" if any_test_failed?(test_results)
47
38
  end
48
39
 
49
- private
40
+ def self.run_tests(runner, group, process_number, options)
41
+ if group.empty?
42
+ {:stdout => '', :exit_status => 0}
43
+ else
44
+ runner.run_tests(group, process_number, options)
45
+ end
46
+ end
47
+
48
+ def self.report_results(runner, test_results)
49
+ results = runner.find_results(test_results.map { |result| result[:stdout] }*"")
50
+ puts ""
51
+ puts runner.summarize_results(results)
52
+ end
53
+
54
+ def self.report_number_of_tests(runner, groups)
55
+ name = runner.test_file_name
56
+ num_processes = groups.size
57
+ num_tests = groups.map(&:size).inject(:+)
58
+ puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
59
+ end
60
+
61
+ #exit with correct status code so rake parallel:test && echo 123 works
62
+ def self.any_test_failed?(test_results)
63
+ test_results.any? { |result| result[:exit_status] != 0 }
64
+ end
65
+
66
+ def self.load_runner_for(lib)
67
+ require "parallel_tests/#{lib}/runner"
68
+ eval("ParallelTests::#{lib.capitalize}::Runner")
69
+ end
50
70
 
51
71
  def self.parse_options!(argv)
52
72
  options = {}
@@ -60,7 +80,7 @@ Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2',
60
80
  Options are:
61
81
  BANNER
62
82
  opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs") { |n| options[:count] = n }
63
- opts.on("-p", '--pattern [PATTERN]', "run tests matching this pattern") { |pattern| options[:pattern] = pattern }
83
+ opts.on("-p", '--pattern [PATTERN]', "run tests matching this pattern") { |pattern| options[:pattern] = /#{pattern}/ }
64
84
  opts.on("--no-sort", "do not sort files before running them") { |no_sort| options[:no_sort] = no_sort }
65
85
  opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run") { |multiply| options[:multiply] = multiply }
66
86
  opts.on("-s [PATTERN]", "--single [PATTERN]", "Run all matching files in only one process") do |pattern|
@@ -1,10 +1,8 @@
1
- # add rake tasks if we are inside Rails 3
2
- if defined?(Rails::Railtie)
3
- module ParallelTests
4
- class Railtie < ::Rails::Railtie
5
- rake_tasks do
6
- load File.expand_path("../tasks.rake", __FILE__)
7
- end
1
+ # rake tasks for Rails 3+
2
+ module ParallelTests
3
+ class Railtie < ::Rails::Railtie
4
+ rake_tasks do
5
+ require "parallel_tests/tasks"
8
6
  end
9
7
  end
10
8
  end
@@ -22,9 +22,8 @@ module ParallelTests
22
22
  cmd or raise("Can't find executables rspec or spec")
23
23
  end
24
24
 
25
- # legacy <-> people log to this file using rspec options
26
25
  def self.runtime_log
27
- 'tmp/parallel_profile.log'
26
+ 'tmp/parallel_runtime_spec.log'
28
27
  end
29
28
 
30
29
  def self.test_file_name
@@ -49,7 +49,7 @@ namespace :parallel do
49
49
  count, pattern, options = ParallelTests.parse_rake_args(args)
50
50
  test_type = (type == 'features' ? 'cucumber' : type)
51
51
  executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')
52
- command = "#{executable} '#{Rails.root}/#{type}' --type #{test_type} -n #{count} -p '#{pattern}' -o '#{options}'"
52
+ command = "#{executable} #{type} --type #{test_type} -n #{count} -p '#{pattern}' -o '#{options}'"
53
53
  abort unless system(command) # allow to chain tasks e.g. rake parallel:spec parallel:features
54
54
  end
55
55
  end
@@ -49,7 +49,7 @@ module ParallelTests
49
49
 
50
50
  def self.find_results(test_output)
51
51
  test_output.split("\n").map {|line|
52
- line = line.gsub(/\.|F|\*/,'')
52
+ line = line.gsub(/\.|F|\*/,'').gsub(/\e\[\d+m/,'')
53
53
  next unless line_is_result?(line)
54
54
  line
55
55
  }.compact
@@ -121,20 +121,18 @@ module ParallelTests
121
121
  def self.find_tests(tests, options={})
122
122
  (tests||[]).map do |file_or_folder|
123
123
  if File.directory?(file_or_folder)
124
- tests_in_folder(file_or_folder, options)
124
+ files = files_in_folder(file_or_folder)
125
+ files.grep(/#{Regexp.escape test_suffix}$/).grep(options[:pattern]||//)
125
126
  else
126
127
  file_or_folder
127
128
  end
128
129
  end.flatten.uniq
129
130
  end
130
131
 
131
- def self.tests_in_folder(folder, options)
132
+ def self.files_in_folder(folder)
132
133
  # follow one symlink and direct children
133
134
  # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
134
- files = Dir["#{folder}/**{,/*/**}/*#{test_suffix}"].uniq
135
- files = files.map{|f| f.sub(folder+'/','') }
136
- files = files.grep(/#{options[:pattern]}/)
137
- files.map{|f| "#{folder}/#{f}" }
135
+ Dir[File.join(folder, "**{,/*/**}/*")].uniq
138
136
  end
139
137
  end
140
138
  end
@@ -1,3 +1,3 @@
1
1
  module ParallelTests
2
- VERSION = Version = '0.7.0.alpha2'
2
+ VERSION = Version = '0.7.0.rc1'
3
3
  end
@@ -143,6 +143,16 @@ describe 'CLI' do
143
143
  result.scan(/ENV-.?-/).should =~ ["ENV--", "ENV-2-", "ENV-3-", "ENV-4-", "ENV-5-"]
144
144
  end
145
145
 
146
+ it "filters test by given pattern and relative paths" do
147
+ write "spec/x_spec.rb", "puts 'XXX'"
148
+ write "spec/y_spec.rb", "puts 'YYY'"
149
+ write "spec/z_spec.rb", "puts 'ZZZ'"
150
+ result = run_tests("spec", :add => '-p "^spec/(x|z)"')
151
+ result.should include('XXX')
152
+ result.should_not include('YYY')
153
+ result.should include('ZZZ')
154
+ end
155
+
146
156
  context "Test::Unit" do
147
157
  it "runs" do
148
158
  write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end"
@@ -97,6 +97,13 @@ EOF
97
97
 
98
98
  call(output).should == ['10 tests, 20 assertions, 0 failures, 0 errors','14 tedsts, 20 assertions, 0 failures, 0 errors']
99
99
  end
100
+
101
+ it "ignores color-codes" do
102
+ output = <<EOF
103
+ 10 tests, 20 assertions, 0 \e[31mfailures, 0 errors
104
+ EOF
105
+ call(output).should == ['10 tests, 20 assertions, 0 failures, 0 errors']
106
+ end
100
107
  end
101
108
 
102
109
  describe :find_tests do
@@ -104,45 +111,90 @@ EOF
104
111
  ParallelTests::Test::Runner.send(:find_tests, *args)
105
112
  end
106
113
 
107
- it "finds test files nested in folders" do
114
+ def with_files(files)
108
115
  begin
109
116
  root = "/tmp/test-find_tests-#{rand(999)}"
110
117
  `mkdir #{root}`
111
- `mkdir #{root}/a`
112
- `mkdir #{root}/b`
113
- `touch #{root}/x_test.rb`
114
- `touch #{root}/a/x_test.rb`
115
- `touch #{root}/a/test.rb`
116
- `touch #{root}/b/y_test.rb`
117
- `touch #{root}/b/test.rb`
118
- `ln -s #{root}/b #{root}/c`
119
- `ln -s #{root}/b #{root}/a/`
120
- call([root]).sort.should == [
121
- "#{root}/a/b/y_test.rb",
122
- "#{root}/a/x_test.rb",
123
- "#{root}/b/y_test.rb",
124
- "#{root}/c/y_test.rb",
125
- "#{root}/x_test.rb"
126
- ]
118
+ files.each do |file|
119
+ parent = "#{root}/#{File.dirname(file)}"
120
+ `mkdir -p #{parent}` unless File.exist?(parent)
121
+ `touch #{root}/#{file}`
122
+ end
123
+ yield root
127
124
  ensure
128
125
  `rm -rf #{root}`
129
126
  end
130
127
  end
131
128
 
132
- it "finds test files in folders by pattern" do
133
- begin
134
- root = "/tmp/test-find_tests-#{rand(999)}"
135
- `mkdir #{root}`
136
- `mkdir #{root}/a`
137
- `touch #{root}/a/x_test.rb`
138
- `touch #{root}/a/y_test.rb`
139
- `touch #{root}/a/z_test.rb`
140
- call([root], :pattern => '^a/(y|z)_test').sort.should == [
141
- "#{root}/a/y_test.rb",
142
- "#{root}/a/z_test.rb",
129
+ def inside_dir(dir)
130
+ old = Dir.pwd
131
+ Dir.chdir dir
132
+ yield
133
+ ensure
134
+ Dir.chdir old
135
+ end
136
+
137
+ it "finds test in folders with appended /" do
138
+ with_files(['b/a_test.rb']) do |root|
139
+ call(["#{root}/"]).sort.should == [
140
+ "#{root}/b/a_test.rb",
143
141
  ]
144
- ensure
145
- `rm -rf #{root}`
142
+ end
143
+ end
144
+
145
+ it "finds test files nested in symlinked folders" do
146
+ with_files(['a/a_test.rb','b/b_test.rb']) do |root|
147
+ `ln -s #{root}/a #{root}/b/link`
148
+ call(["#{root}/b"]).sort.should == [
149
+ "#{root}/b/b_test.rb",
150
+ "#{root}/b/link/a_test.rb",
151
+ ]
152
+ end
153
+ end
154
+
155
+ it "finds test files nested in different folders" do
156
+ with_files(['a/a_test.rb','b/b_test.rb', 'c/c_test.rb']) do |root|
157
+ call(["#{root}/a", "#{root}/b"]).sort.should == [
158
+ "#{root}/a/a_test.rb",
159
+ "#{root}/b/b_test.rb",
160
+ ]
161
+ end
162
+ end
163
+
164
+ it "only finds tests in folders" do
165
+ with_files(['a/a_test.rb', 'a/test.rb', 'a/test_helper.rb']) do |root|
166
+ call(["#{root}/a"]).sort.should == [
167
+ "#{root}/a/a_test.rb"
168
+ ]
169
+ end
170
+ end
171
+
172
+ it "finds tests in nested folders" do
173
+ with_files(['a/b/c/d/a_test.rb']) do |root|
174
+ call(["#{root}/a"]).sort.should == [
175
+ "#{root}/a/b/c/d/a_test.rb"
176
+ ]
177
+ end
178
+ end
179
+
180
+ it "does not expand paths" do
181
+ with_files(['a/x_test.rb']) do |root|
182
+ inside_dir root do
183
+ call(['a']).sort.should == [
184
+ "a/x_test.rb"
185
+ ]
186
+ end
187
+ end
188
+ end
189
+
190
+ it "finds test files in folders by pattern" do
191
+ with_files(['a/x_test.rb','a/y_test.rb','a/z_test.rb']) do |root|
192
+ inside_dir root do
193
+ call(["a"], :pattern => /^a\/(y|z)_test/).sort.should == [
194
+ "a/y_test.rb",
195
+ "a/z_test.rb",
196
+ ]
197
+ end
146
198
  end
147
199
  end
148
200
 
metadata CHANGED
@@ -1,36 +1,49 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
- version: !ruby/object:Gem::Version
4
- version: 0.7.0.alpha2
3
+ version: !ruby/object:Gem::Version
4
+ hash: 161060265
5
5
  prerelease: 6
6
+ segments:
7
+ - 0
8
+ - 7
9
+ - 0
10
+ - rc
11
+ - 1
12
+ version: 0.7.0.rc1
6
13
  platform: ruby
7
- authors:
14
+ authors:
8
15
  - Michael Grosser
9
16
  autorequire:
10
17
  bindir: bin
11
18
  cert_chain: []
12
- date: 2012-02-26 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: parallel
16
- requirement: &16386080 !ruby/object:Gem::Requirement
19
+
20
+ date: 2012-02-26 00:00:00 Z
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
17
24
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
23
32
  prerelease: false
24
- version_requirements: *16386080
33
+ requirement: *id001
34
+ type: :runtime
35
+ name: parallel
25
36
  description:
26
37
  email: michael@grosser.it
27
- executables:
38
+ executables:
28
39
  - parallel_cucumber
29
40
  - parallel_spec
30
41
  - parallel_test
31
42
  extensions: []
43
+
32
44
  extra_rdoc_files: []
33
- files:
45
+
46
+ files:
34
47
  - .gitignore
35
48
  - Gemfile
36
49
  - Gemfile.lock
@@ -67,31 +80,39 @@ files:
67
80
  - spec/parallel_tests_spec.rb
68
81
  - spec/spec_helper.rb
69
82
  homepage: http://github.com/grosser/parallel_tests
70
- licenses:
83
+ licenses:
71
84
  - MIT
72
85
  post_install_message:
73
86
  rdoc_options: []
74
- require_paths:
87
+
88
+ require_paths:
75
89
  - lib
76
- required_ruby_version: !ruby/object:Gem::Requirement
90
+ required_ruby_version: !ruby/object:Gem::Requirement
77
91
  none: false
78
- requirements:
79
- - - ! '>='
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
- segments:
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
83
97
  - 0
84
- hash: -3495402516497603622
85
- required_rubygems_version: !ruby/object:Gem::Requirement
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
100
  none: false
87
- requirements:
88
- - - ! '>'
89
- - !ruby/object:Gem::Version
101
+ requirements:
102
+ - - ">"
103
+ - !ruby/object:Gem::Version
104
+ hash: 25
105
+ segments:
106
+ - 1
107
+ - 3
108
+ - 1
90
109
  version: 1.3.1
91
110
  requirements: []
111
+
92
112
  rubyforge_project:
93
113
  rubygems_version: 1.8.15
94
114
  signing_key:
95
115
  specification_version: 3
96
116
  summary: Run Test::Unit / RSpec / Cucumber in parallel
97
117
  test_files: []
118
+