parallel_tests 0.7.0.alpha2 → 0.7.0.rc1

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