parallel_tests 0.7.0.alpha → 0.7.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- parallel_tests (0.7.0.alpha)
4
+ parallel_tests (0.7.0.alpha2)
5
5
  parallel
6
6
 
7
7
  GEM
data/Readme.md CHANGED
@@ -3,8 +3,9 @@ ParallelTests splits tests into even groups(by number of tests or runtime) and r
3
3
 
4
4
  Setup for Rails
5
5
  ===============
6
+ [still using Rails 2?](https://github.com/grosser/parallel_tests/blob/master/ReadmeRails2.md)
7
+
6
8
  ## Install
7
- ### Rails 3
8
9
  If you use RSpec: ensure you got >= 2.4
9
10
 
10
11
  As gem
@@ -19,28 +20,6 @@ OR as plugin
19
20
  # add to Gemfile
20
21
  gem "parallel", :group => :development
21
22
 
22
-
23
- ### Rails 2
24
-
25
- As gem
26
-
27
- gem install parallel_tests
28
-
29
- # add to config/environments/development.rb
30
- config.gem "parallel_tests"
31
-
32
- # add to Rakefile
33
- begin; require 'parallel_tests/tasks'; rescue LoadError; end
34
-
35
- OR as plugin
36
-
37
- gem install parallel
38
-
39
- # add to config/environments/development.rb
40
- config.gem "parallel"
41
-
42
- ./script/plugin install git://github.com/grosser/parallel_tests.git
43
-
44
23
  ## Setup
45
24
  ParallelTests uses 1 database per test-process, 2 processes will use `*_test` and `*_test2`.
46
25
 
@@ -91,17 +70,14 @@ Log test runtime to give each process the same runtime.
91
70
 
92
71
  Rspec: Add to your `.rspec_parallel` (or `.rspec`) :
93
72
 
94
- RSpec 1.x:
95
- --format progress
96
- --require parallel_tests/spec/runtime_logger
97
- --format ParallelTests::Spec::RuntimeLogger:tmp/parallel_profile.log
98
- RSpec >= 2.4:
73
+ RSpec
99
74
  If installed as plugin: -I vendor/plugins/parallel_tests/lib
100
75
  --format progress
101
76
  --format ParallelTests::Spec::RuntimeLogger --out tmp/parallel_profile.log
102
77
 
103
78
  Test::Unit: Add to your `test_helper.rb`:
104
- require 'parallel_tests/runtime_logger'
79
+
80
+ require 'parallel_tests/test/runtime_logger'
105
81
 
106
82
 
107
83
  SpecSummaryLogger
@@ -111,11 +87,7 @@ This logger logs the test output without the different processes overwriting eac
111
87
 
112
88
  Add the following to your `.rspec_parallel` (or `.rspec`) :
113
89
 
114
- RSpec 1.x:
115
- --format progress
116
- --require parallel_tests/spec/summary_logger
117
- --format ParallelTests::Spec::SummaryLogger:tmp/spec_summary.log
118
- RSpec >= 2.2:
90
+ RSpec:
119
91
  If installed as plugin: -I vendor/plugins/parallel_tests/lib
120
92
  --format progress
121
93
  --format ParallelTests::Spec::SummaryLogger --out tmp/spec_summary.log
@@ -131,23 +103,21 @@ E.g.
131
103
 
132
104
  Add the following to your `.rspec_parallel` (or `.rspec`) :
133
105
 
134
- RSpec 1.x:
135
- --format progress
136
- --require parallel_tests/spec/failures_logger
137
- --format ParallelTests::Spec::FailuresLogger:tmp/failing_specs.log
138
- RSpec >= 2.4:
106
+ RSpec:
139
107
  If installed as plugin: -I vendor/plugins/parallel_tests/lib
140
108
  --format progress
141
109
  --format ParallelTests::Spec::FailuresLogger --out tmp/failing_specs.log
142
110
 
143
111
  Setup for non-rails
144
112
  ===================
145
- sudo gem install parallel_tests
113
+ gem install parallel_tests
146
114
  # go to your project dir
147
- parallel_test OR parallel_spec OR parallel_cucumber
148
- # [Optional] use ENV['TEST_ENV_NUMBER'] inside your tests to select separate db/memcache/etc.
115
+ parallel_test test/
116
+ parallel_spec spec/
117
+ parallel_cucumber features/
149
118
 
150
- [optional] Only run selected files & folders:
119
+ - use ENV['TEST_ENV_NUMBER'] inside your tests to select separate db/memcache/etc.
120
+ - Only run selected files & folders:
151
121
 
152
122
  parallel_test test/bar test/baz/foo_text.rb
153
123
 
@@ -157,15 +127,14 @@ Options are:
157
127
  -p, --path [PATH] run tests inside this path only
158
128
  --no-sort do not sort files before running them
159
129
  -m, --multiply-processes [FLOAT] use given number as a multiplier of processes to run
160
- -r, --root [PATH] execute test commands from this path
161
130
  -e, --exec [COMMAND] execute this code parallel and with ENV['TEST_ENV_NUM']
162
131
  -o, --test-options '[OPTIONS]' execute test commands with those options
163
- -t, --type [TYPE] which type of tests to run? test, spec or features
132
+ -t, --type [TYPE] test(default) / spec / cucumber
164
133
  --non-parallel execute same commands but do not in parallel, needs --exec
165
134
  -v, --version Show Version
166
135
  -h, --help Show this.
167
136
 
168
- You can run any kind of code with -e / --execute
137
+ You can run any kind of code in parallel with -e / --execute
169
138
 
170
139
  parallel_test -n 5 -e 'ruby -e "puts %[hello from process #{ENV[:TEST_ENV_NUMBER.to_s].inspect}]"'
171
140
  hello from process "2"
@@ -183,11 +152,9 @@ You can run any kind of code with -e / --execute
183
152
  TIPS
184
153
  ====
185
154
  - [Capybara + Selenium] add to env.rb: `Capybara.server_port = 8888 + ENV['TEST_ENV_NUMBER'].to_i`
186
- - [RSpec] add a `spec/parallel_spec.opts` to use different options, e.g. no --drb (default: `spec/spec.opts`)
187
- - [RSpec] if something looks fishy try to delete `script/spec`
188
- - [RSpec] if `script/spec` is missing parallel:spec uses just `spec` (which solves some issues with double-loaded environment.rb)
189
- - [RSpec] 'script/spec_server' or [spork](http://github.com/timcharper/spork/tree/master) do not work in parallel
190
- - [RSpec] `./script/generate rspec` if you are running rspec from gems (this plugin uses script/spec which may fail if rspec files are outdated)
155
+ - [RSpec] add a `.rspec_parallel` to use different options, e.g. **no --drb**
156
+ - [RSpec] delete `script/spec`
157
+ - [RSpec] [spork](https://github.com/timcharper/spork) does not work in parallel
191
158
  - [RSpec] remove --loadby from you spec/*.opts
192
159
  - [Bundler] if you have a `Gemfile` then `bundle exec` will be used to run tests
193
160
  - [Capybara setup](https://github.com/grosser/parallel_tests/wiki)
@@ -196,11 +163,11 @@ TIPS
196
163
  - [SQL schema format] use :ruby schema format to get faster parallel:prepare`
197
164
  - [ActiveRecord] if you do not have `db:abort_if_pending_migrations` add this to your Rakefile: `task('db:abort_if_pending_migrations'){}`
198
165
  - `export PARALLEL_TEST_PROCESSORS=X` in your environment and parallel_tests will use this number of processors by default
199
- - with zsh this would be `rake "parallel:prepare[3]"`
166
+ - [ZSH] use quotes to use rake arguments `rake "parallel:prepare[3]"`
200
167
 
201
168
  TODO
202
169
  ====
203
- - move everything Rails 2 related to a e.g. Rails2Readme.md and link it
170
+ - document how to use cucumber runtime logger
204
171
  - unify runtime-log location
205
172
  - add tests for cucumber runtime formatter
206
173
  - make jRuby compatible [basics](http://yehudakatz.com/2009/07/01/new-rails-isolation-testing/)
@@ -240,5 +207,4 @@ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-
240
207
 
241
208
  [Michael Grosser](http://grosser.it)<br/>
242
209
  michael@grosser.it<br/>
243
- Hereby placed under public domain, do what you want, just do not hold me accountable...<br/>
244
- [![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)
210
+ License: MIT
@@ -0,0 +1,44 @@
1
+ ### Install
2
+
3
+ As gem
4
+
5
+ gem install parallel_tests
6
+
7
+ # add to config/environments/development.rb
8
+ config.gem "parallel_tests"
9
+
10
+ # add to Rakefile
11
+ begin; require 'parallel_tests/tasks'; rescue LoadError; end
12
+
13
+ OR as plugin
14
+
15
+ gem install parallel
16
+
17
+ # add to config/environments/development.rb
18
+ config.gem "parallel"
19
+
20
+ ./script/plugin install git://github.com/grosser/parallel_tests.git
21
+
22
+ Even process runtimes
23
+ -----------------
24
+
25
+ RSpec 1.x:
26
+ --format progress
27
+ --require parallel_tests/spec/runtime_logger
28
+ --format ParallelTests::Spec::RuntimeLogger:tmp/parallel_profile.log
29
+
30
+ SpecSummaryLogger
31
+ --------------------
32
+
33
+ RSpec 1.x:
34
+ --format progress
35
+ --require parallel_tests/spec/summary_logger
36
+ --format ParallelTests::Spec::SummaryLogger:tmp/spec_summary.log
37
+
38
+ SpecFailuresLogger
39
+ -----------------------
40
+
41
+ RSpec 1.x:
42
+ --format progress
43
+ --require parallel_tests/spec/failures_logger
44
+ --format ParallelTests::Spec::FailuresLogger:tmp/failing_specs.log
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env ruby
2
- exec "#{File.join(File.dirname(__FILE__), 'parallel_test')} -t features #{ARGV * ' '}"
2
+ exec "#{File.join(File.dirname(__FILE__), 'parallel_test')} -t cucumber #{ARGV * ' '}"
@@ -4,6 +4,51 @@ require 'parallel_tests/test/runner'
4
4
  module ParallelTest
5
5
  module CLI
6
6
  def self.run(argv)
7
+ options = parse_options!(argv)
8
+ test_results = nil
9
+
10
+ num_processes = ParallelTests.determine_number_of_processes(options[:count])
11
+ num_processes = num_processes * (options[:multiply] || 1)
12
+
13
+ if options[:execute]
14
+ execute_shell_command_in_parallel(options[:execute], num_processes, options)
15
+ 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)
41
+ end
42
+
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
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def self.parse_options!(argv)
7
52
  options = {}
8
53
  OptionParser.new do |opts|
9
54
  opts.banner = <<BANNER
@@ -18,14 +63,13 @@ BANNER
18
63
  opts.on("-p", '--pattern [PATTERN]', "run tests matching this pattern") { |pattern| options[:pattern] = pattern }
19
64
  opts.on("--no-sort", "do not sort files before running them") { |no_sort| options[:no_sort] = no_sort }
20
65
  opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run") { |multiply| options[:multiply] = multiply }
21
- opts.on("-r", '--root [PATH]', "execute test commands from this path") { |path| options[:root] = path }
22
66
  opts.on("-s [PATTERN]", "--single [PATTERN]", "Run all matching files in only one process") do |pattern|
23
67
  options[:single_process] ||= []
24
68
  options[:single_process] << /#{pattern}/
25
69
  end
26
70
  opts.on("-e", '--exec [COMMAND]', "execute this code parallel and with ENV['TEST_ENV_NUM']") { |path| options[:execute] = path }
27
71
  opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options") { |arg| options[:test_options] = arg }
28
- opts.on("-t", "--type [TYPE]", "which type of tests to run? test, spec or features") { |type| options[:type] = type }
72
+ opts.on("-t", "--type [TYPE]", "test(default) / spec / cucumber") { |type| options[:type] = type }
29
73
  opts.on("--non-parallel", "execute same commands but do not in parallel, needs --exec") { options[:non_parallel] = true }
30
74
  opts.on("--chunk-timeout [TIMEOUT]", "timeout before re-printing the output of a child-process") { |timeout| options[:chunk_timeout] = timeout.to_f }
31
75
  opts.on('-v', '--version', 'Show Version') { puts ParallelTests::VERSION; exit }
@@ -34,69 +78,30 @@ BANNER
34
78
 
35
79
  raise "--no-sort and --single-process are not supported" if options[:no_sort] and options[:single_process]
36
80
 
37
- # get files to run from arguments
38
- options[:files] = argv if argv.size > 0
39
-
40
- num_processes = ParallelTests.determine_number_of_processes(options[:count])
41
- num_processes = num_processes * (options[:multiply] || 1)
81
+ options[:files] = argv
82
+ options
83
+ end
42
84
 
43
- if options[:execute]
44
- runs = (0...num_processes).to_a
45
- results = if options[:non_parallel]
46
- runs.map do |i|
47
- ParallelTests::Test::Runner.execute_command(options[:execute], i, options)
48
- end
49
- else
50
- Parallel.map(runs, :in_processes => num_processes) do |i|
51
- ParallelTests::Test::Runner.execute_command(options[:execute], i, options)
52
- end
53
- end.flatten
54
- abort if results.any? { |r| r[:exit_status] != 0 }
85
+ def self.execute_shell_command_in_parallel(command, num_processes, options)
86
+ runs = (0...num_processes).to_a
87
+ results = if options[:non_parallel]
88
+ runs.map do |i|
89
+ ParallelTests::Test::Runner.execute_command(command, i, options)
90
+ end
55
91
  else
56
- lib, name, task = {
57
- 'test' => ["test", "test", "test"],
58
- 'spec' => ["spec", "spec", "spec"],
59
- 'features' => ["cucumber", "feature", "features"]
60
- }[options[:type]||'test']
61
-
62
- require "parallel_tests/#{lib}/runner"
63
- klass = eval("ParallelTests::#{lib.capitalize}::Runner")
64
-
65
- start = Time.now
66
-
67
- tests_folder = task
68
- tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
69
-
70
- groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, options)
71
- num_processes = groups.size
72
-
73
- #adjust processes to groups
74
- abort "no #{name}s found!" if groups.size == 0
75
-
76
- num_tests = groups.inject(0) { |sum, item| sum + item.size }
77
- puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
78
-
79
- test_results = Parallel.map(groups, :in_processes => num_processes) do |group|
80
- if group.empty?
81
- {:stdout => '', :exit_status => 0}
82
- else
83
- klass.run_tests(group, groups.index(group), options)
84
- end
92
+ Parallel.map(runs, :in_processes => num_processes) do |i|
93
+ ParallelTests::Test::Runner.execute_command(command, i, options)
85
94
  end
95
+ end.flatten
86
96
 
87
- #parse and print results
88
- results = klass.find_results(test_results.map { |result| result[:stdout] }*"")
89
- puts ""
90
- puts klass.summarize_results(results)
91
-
92
- #report total time taken
93
- puts ""
94
- puts "Took #{Time.now - start} seconds"
97
+ abort if results.any? { |r| r[:exit_status] != 0 }
98
+ end
95
99
 
96
- #exit with correct status code so rake parallel:test && echo 123 works
97
- failed = test_results.any? { |result| result[:exit_status] != 0 }
98
- abort "#{name.capitalize}s Failed" if failed
99
- end
100
+ def self.report_time_taken
101
+ start = Time.now
102
+ yield
103
+ puts ""
104
+ puts "Took #{Time.now - start} seconds"
100
105
  end
101
106
  end
102
107
  end
@@ -26,7 +26,9 @@ module ParallelTests
26
26
  'tmp/parallel_runtime_cucumber.log'
27
27
  end
28
28
 
29
- protected
29
+ def self.test_file_name
30
+ "feature"
31
+ end
30
32
 
31
33
  def self.test_suffix
32
34
  ".feature"
@@ -27,7 +27,15 @@ module ParallelTests
27
27
  'tmp/parallel_profile.log'
28
28
  end
29
29
 
30
- protected
30
+ def self.test_file_name
31
+ "spec"
32
+ end
33
+
34
+ def self.test_suffix
35
+ "_spec.rb"
36
+ end
37
+
38
+ private
31
39
 
32
40
  # so it can be stubbed....
33
41
  def self.run(cmd)
@@ -47,10 +55,6 @@ module ParallelTests
47
55
  return unless options_file
48
56
  "-O #{options_file}"
49
57
  end
50
-
51
- def self.test_suffix
52
- "_spec.rb"
53
- end
54
58
  end
55
59
  end
56
60
  end
@@ -47,8 +47,9 @@ namespace :parallel do
47
47
  $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
48
48
  require "parallel_tests"
49
49
  count, pattern, options = ParallelTests.parse_rake_args(args)
50
+ test_type = (type == 'features' ? 'cucumber' : type)
50
51
  executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')
51
- command = "#{executable} --type #{type} -n #{count} -p '#{pattern}' -r '#{Rails.root}' -o '#{options}'"
52
+ command = "#{executable} '#{Rails.root}/#{type}' --type #{test_type} -n #{count} -p '#{pattern}' -o '#{options}'"
52
53
  abort unless system(command) # allow to chain tasks e.g. rake parallel:spec parallel:features
53
54
  end
54
55
  end
@@ -1,9 +1,36 @@
1
1
  module ParallelTests
2
2
  module Test
3
3
  class Runner
4
+ # --- usually overwritten by other runners
5
+
6
+ def self.runtime_log
7
+ 'tmp/parallel_runtime_test.log'
8
+ end
9
+
10
+ def self.test_suffix
11
+ "_test.rb"
12
+ end
13
+
14
+ def self.test_file_name
15
+ "test"
16
+ end
17
+
18
+ def self.run_tests(test_files, process_number, options)
19
+ require_list = test_files.map { |filename| %{"#{File.expand_path filename}"} }.join(",")
20
+ cmd = "ruby -Itest -e '[#{require_list}].each {|f| require f }' -- #{options[:test_options]}"
21
+ execute_command(cmd, process_number, options)
22
+ end
23
+
24
+ def self.line_is_result?(line)
25
+ line =~ /\d+ failure/
26
+ end
27
+
28
+ # --- usually used by other runners
29
+
4
30
  # finds all tests and partitions them into groups
5
- def self.tests_in_groups(root, num_groups, options={})
6
- tests = find_tests(root, options)
31
+ def self.tests_in_groups(tests, num_groups, options={})
32
+ tests = find_tests(tests, options)
33
+
7
34
  if options[:no_sort] == true
8
35
  Grouper.in_groups(tests, num_groups)
9
36
  else
@@ -12,12 +39,6 @@ module ParallelTests
12
39
  end
13
40
  end
14
41
 
15
- def self.run_tests(test_files, process_number, options)
16
- require_list = test_files.map { |filename| %{"#{File.expand_path filename}"} }.join(",")
17
- cmd = "ruby -Itest -e '[#{require_list}].each {|f| require f }' -- #{options[:test_options]}"
18
- execute_command(cmd, process_number, options)
19
- end
20
-
21
42
  def self.execute_command(cmd, process_number, options)
22
43
  cmd = "TEST_ENV_NUMBER=#{test_env_number(process_number)} ; export TEST_ENV_NUMBER; #{cmd}"
23
44
  f = open("|#{cmd}", 'r')
@@ -38,10 +59,6 @@ module ParallelTests
38
59
  process_number == 0 ? '' : process_number + 1
39
60
  end
40
61
 
41
- def self.runtime_log
42
- 'tmp/parallel_runtime_test.log'
43
- end
44
-
45
62
  def self.summarize_results(results)
46
63
  results = results.join(' ').gsub(/s\b/,'') # combine and singularize results
47
64
  counts = results.scan(/(\d+) (\w+)/)
@@ -83,14 +100,6 @@ module ParallelTests
83
100
  all
84
101
  end
85
102
 
86
- def self.line_is_result?(line)
87
- line =~ /\d+ failure/
88
- end
89
-
90
- def self.test_suffix
91
- "_test.rb"
92
- end
93
-
94
103
  def self.with_runtime_info(tests)
95
104
  lines = File.read(runtime_log).split("\n") rescue []
96
105
 
@@ -109,17 +118,23 @@ module ParallelTests
109
118
  end
110
119
  end
111
120
 
112
- def self.find_tests(root, options={})
113
- if root.is_a?(Array)
114
- root
115
- else
116
- # follow one symlink and direct children
117
- # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
118
- files = Dir["#{root}/**{,/*/**}/*#{test_suffix}"].uniq
119
- files = files.map{|f| f.sub(root+'/','') }
120
- files = files.grep(/#{options[:pattern]}/)
121
- files.map{|f| "#{root}/#{f}" }
122
- end
121
+ def self.find_tests(tests, options={})
122
+ (tests||[]).map do |file_or_folder|
123
+ if File.directory?(file_or_folder)
124
+ tests_in_folder(file_or_folder, options)
125
+ else
126
+ file_or_folder
127
+ end
128
+ end.flatten.uniq
129
+ end
130
+
131
+ def self.tests_in_folder(folder, options)
132
+ # follow one symlink and direct children
133
+ # 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}" }
123
138
  end
124
139
  end
125
140
  end
@@ -1,3 +1,3 @@
1
1
  module ParallelTests
2
- VERSION = Version = '0.7.0.alpha'
2
+ VERSION = Version = '0.7.0.alpha2'
3
3
  end
@@ -3,7 +3,7 @@ name = "parallel_tests"
3
3
  require "#{name}/version"
4
4
 
5
5
  Gem::Specification.new name, ParallelTests::VERSION do |s|
6
- s.summary = "Run tests / specs / features in parallel"
6
+ s.summary = "Run Test::Unit / RSpec / Cucumber in parallel"
7
7
  s.authors = ["Michael Grosser"]
8
8
  s.email = "michael@grosser.it"
9
9
  s.homepage = "http://github.com/grosser/#{name}"
@@ -15,7 +15,7 @@ describe 'CLI' do
15
15
 
16
16
  def write(file, content)
17
17
  path = "#{folder}/#{file}"
18
- `mkdir -p #{File.dirname(path)}` unless File.exist?(File.dirname(path))
18
+ ensure_folder File.dirname(path)
19
19
  File.open(path, 'w'){|f| f.write content }
20
20
  path
21
21
  end
@@ -28,15 +28,22 @@ describe 'CLI' do
28
28
  "#{bin_folder}/parallel_test"
29
29
  end
30
30
 
31
- def run_tests(options={})
31
+ def ensure_folder(folder)
32
+ `mkdir -p #{folder}` unless File.exist?(folder)
33
+ end
34
+
35
+ def run_tests(test_folder, options={})
36
+ ensure_folder folder
32
37
  processes = "-n #{options[:processes]||2}" unless options[:processes] == false
33
- `cd #{folder} && #{options[:export]} #{executable} --chunk-timeout 999 -t #{options[:type] || 'spec'} #{processes} #{options[:add]} 2>&1`
38
+ result = `cd #{folder} && #{options[:export]} #{executable} #{test_folder} --chunk-timeout 999 -t #{options[:type] || 'spec'} #{processes} #{options[:add]} 2>&1`
39
+ raise "FAILED #{result}" if $?.success? == !!options[:fail]
40
+ result
34
41
  end
35
42
 
36
43
  it "runs tests in parallel" do
37
44
  write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
38
45
  write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){puts "TEST2"}}'
39
- result = run_tests
46
+ result = run_tests "spec"
40
47
 
41
48
  # test ran and gave their puts
42
49
  result.should include('TEST1')
@@ -47,12 +54,11 @@ describe 'CLI' do
47
54
  result.scan('2 examples, 0 failures').size.should == 1 # 1 summary
48
55
  result.scan(/Finished in \d+\.\d+ seconds/).size.should == 2
49
56
  result.scan(/Took \d+\.\d+ seconds/).size.should == 1 # parallel summary
50
- $?.success?.should == true
51
57
  end
52
58
 
53
59
  it "does not run any tests if there are none" do
54
60
  write 'spec/xxx_spec.rb', '1'
55
- result = run_tests
61
+ result = run_tests "spec"
56
62
  result.should include('No examples found')
57
63
  result.should include('Took')
58
64
  end
@@ -60,12 +66,11 @@ describe 'CLI' do
60
66
  it "fails when tests fail" do
61
67
  write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
62
68
  write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){1.should == 2}}'
63
- result = run_tests
69
+ result = run_tests "spec", :fail => true
64
70
 
65
71
  result.scan('1 example, 1 failure').size.should == 1
66
72
  result.scan('1 example, 0 failure').size.should == 1
67
73
  result.scan('2 examples, 1 failure').size.should == 1
68
- $?.success?.should == false
69
74
  end
70
75
 
71
76
  it "can exec given commands with ENV['TEST_ENV_NUM']" do
@@ -101,7 +106,7 @@ describe 'CLI' do
101
106
  write "spec/xxx#{i}_spec.rb", 'describe("it"){it("should"){sleep 5}}; $stderr.puts ENV["TEST_ENV_NUMBER"]'
102
107
  }
103
108
  t = Time.now
104
- run_tests(:processes => 2)
109
+ run_tests("spec", :processes => 2)
105
110
  expected = 10
106
111
  (Time.now - t).should <= expected
107
112
  end
@@ -110,16 +115,22 @@ describe 'CLI' do
110
115
  write "spec/x1_spec.rb", "puts '111'"
111
116
  write "spec/x2_spec.rb", "puts '222'"
112
117
  write "spec/x3_spec.rb", "puts '333'"
113
- result = run_tests(:add => 'spec/x1_spec.rb spec/x3_spec.rb')
118
+ result = run_tests("spec/x1_spec.rb spec/x3_spec.rb")
114
119
  result.should include('111')
115
120
  result.should include('333')
116
121
  result.should_not include('222')
117
122
  end
118
123
 
124
+ it "runs successfully without any files" do
125
+ results = run_tests("")
126
+ results.should include("2 processes for 0 specs")
127
+ results.should include("Took")
128
+ end
129
+
119
130
  it "can run with test-options" do
120
131
  write "spec/x1_spec.rb", "111"
121
132
  write "spec/x2_spec.rb", "111"
122
- result = run_tests(:add => "--test-options ' --version'", :processes => 2)
133
+ result = run_tests("spec", :add => "--test-options ' --version'", :processes => 2)
123
134
  result.should =~ /\d+\.\d+\.\d+.*\d+\.\d+\.\d+/m # prints version twice
124
135
  end
125
136
 
@@ -128,23 +139,28 @@ describe 'CLI' do
128
139
  processes.times{|i|
129
140
  write "spec/x#{i}_spec.rb", "puts %{ENV-\#{ENV['TEST_ENV_NUMBER']}-}"
130
141
  }
131
- result = run_tests(:export => "PARALLEL_TEST_PROCESSORS=#{processes}", :processes => processes)
142
+ result = run_tests("spec", :export => "PARALLEL_TEST_PROCESSORS=#{processes}", :processes => processes)
132
143
  result.scan(/ENV-.?-/).should =~ ["ENV--", "ENV-2-", "ENV-3-", "ENV-4-", "ENV-5-"]
133
144
  end
134
145
 
135
146
  context "Test::Unit" do
136
147
  it "runs" do
137
148
  write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end"
138
- result = run_tests(:type => :test)
149
+ result = run_tests("test", :type => :test)
139
150
  result.should include('1 test')
140
- $?.success?.should == true
141
151
  end
142
152
 
143
153
  it "passes test options" do
144
154
  write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end"
145
- result = run_tests(:type => :test, :add => '--test-options "-v"')
155
+ result = run_tests("test", :type => :test, :add => '--test-options "-v"')
146
156
  result.should include('test_xxx') # verbose output of every test
147
157
  end
158
+
159
+ it "runs successfully without any files" do
160
+ results = run_tests("", :type => "test")
161
+ results.should include("2 processes for 0 tests")
162
+ results.should include("Took")
163
+ end
148
164
  end
149
165
 
150
166
  context "Cucumber" do
@@ -154,8 +170,7 @@ describe 'CLI' do
154
170
  write "features/b.feature", "Feature: xxx\n Scenario: xxx\n Given I FAIL"
155
171
  write "features/steps/a.rb", "Given('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }"
156
172
 
157
- result = run_tests :type => 'features', :add => '--pattern good'
158
- $?.success?.should == true
173
+ result = run_tests "features", :type => "cucumber", :add => '--pattern good'
159
174
 
160
175
  result.should include('YOUR TEST ENV IS 2!')
161
176
  result.should include('YOUR TEST ENV IS !')
@@ -167,9 +182,14 @@ describe 'CLI' do
167
182
  2.times{|i|
168
183
  write "features/good#{i}.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
169
184
  }
170
- result = run_tests :type => 'features', :add => '-n 3'
171
- $?.success?.should == true
185
+ result = run_tests "features", :type => "cucumber", :add => '-n 3'
172
186
  result.scan(/YOUR TEST ENV IS \d?!/).sort.should == ["YOUR TEST ENV IS !", "YOUR TEST ENV IS 2!"]
173
187
  end
188
+
189
+ it "runs successfully without any files" do
190
+ results = run_tests("", :type => "cucumber")
191
+ results.should include("2 processes for 0 features")
192
+ results.should include("Took")
193
+ end
174
194
  end
175
195
  end
@@ -104,11 +104,7 @@ EOF
104
104
  ParallelTests::Test::Runner.send(:find_tests, *args)
105
105
  end
106
106
 
107
- it "returns if root is an array" do
108
- call([1]).should == [1]
109
- end
110
-
111
- it "finds all test files" do
107
+ it "finds test files nested in folders" do
112
108
  begin
113
109
  root = "/tmp/test-find_tests-#{rand(999)}"
114
110
  `mkdir #{root}`
@@ -121,7 +117,7 @@ EOF
121
117
  `touch #{root}/b/test.rb`
122
118
  `ln -s #{root}/b #{root}/c`
123
119
  `ln -s #{root}/b #{root}/a/`
124
- call(root).sort.should == [
120
+ call([root]).sort.should == [
125
121
  "#{root}/a/b/y_test.rb",
126
122
  "#{root}/a/x_test.rb",
127
123
  "#{root}/b/y_test.rb",
@@ -133,7 +129,7 @@ EOF
133
129
  end
134
130
  end
135
131
 
136
- it "finds files by pattern" do
132
+ it "finds test files in folders by pattern" do
137
133
  begin
138
134
  root = "/tmp/test-find_tests-#{rand(999)}"
139
135
  `mkdir #{root}`
@@ -141,7 +137,7 @@ EOF
141
137
  `touch #{root}/a/x_test.rb`
142
138
  `touch #{root}/a/y_test.rb`
143
139
  `touch #{root}/a/z_test.rb`
144
- call(root, :pattern => '^a/(y|z)_test').sort.should == [
140
+ call([root], :pattern => '^a/(y|z)_test').sort.should == [
145
141
  "#{root}/a/y_test.rb",
146
142
  "#{root}/a/z_test.rb",
147
143
  ]
@@ -149,6 +145,22 @@ EOF
149
145
  `rm -rf #{root}`
150
146
  end
151
147
  end
148
+
149
+ it "finds nothing if I pass nothing" do
150
+ call(nil).should == []
151
+ end
152
+
153
+ it "finds nothing if I pass nothing (empty array)" do
154
+ call([]).should == []
155
+ end
156
+
157
+ it "keeps invalid files" do
158
+ call(['baz']).should == ['baz']
159
+ end
160
+
161
+ it "discards duplicates" do
162
+ call(['baz','baz']).should == ['baz']
163
+ end
152
164
  end
153
165
 
154
166
  describe :summarize_results do
@@ -75,6 +75,13 @@ def test_tests_in_groups(klass, folder, suffix)
75
75
  `rm -f #{klass.runtime_log}`
76
76
  end
77
77
 
78
+ def setup_runtime_log
79
+ File.open(@log,'w') do |f|
80
+ @files[1..-1].each{|file| f.puts "#{file}:#{@files.index(file)}"}
81
+ f.puts "#{@files[0]}:10"
82
+ end
83
+ end
84
+
78
85
  it "groups when given an array of files" do
79
86
  list_of_files = Dir["#{test_root}/**/*#{suffix}"]
80
87
  found = klass.with_runtime_info(list_of_files)
@@ -82,38 +89,31 @@ def test_tests_in_groups(klass, folder, suffix)
82
89
  end
83
90
 
84
91
  it "finds all tests" do
85
- found = klass.tests_in_groups(test_root, 1)
92
+ found = klass.tests_in_groups([test_root], 1)
86
93
  all = [ Dir["#{test_root}/**/*#{suffix}"] ]
87
94
  (found.flatten - all.flatten).should == []
88
95
  end
89
96
 
90
97
  it "partitions them into groups by equal size" do
91
- groups = klass.tests_in_groups(test_root, 2)
98
+ groups = klass.tests_in_groups([test_root], 2)
92
99
  groups.map{|g| size_of(g)}.should == [400, 400]
93
100
  end
94
101
 
95
102
  it 'should partition correctly with a group size of 4' do
96
- groups = klass.tests_in_groups(test_root, 4)
103
+ groups = klass.tests_in_groups([test_root], 4)
97
104
  groups.map{|g| size_of(g)}.should == [200, 200, 200, 200]
98
105
  end
99
106
 
100
107
  it 'should partition correctly with an uneven group size' do
101
- groups = klass.tests_in_groups(test_root, 3)
108
+ groups = klass.tests_in_groups([test_root], 3)
102
109
  groups.map{|g| size_of(g)}.should =~ [300, 300, 200]
103
110
  end
104
111
 
105
- def setup_runtime_log
106
- File.open(@log,'w') do |f|
107
- @files[1..-1].each{|file| f.puts "#{file}:#{@files.index(file)}"}
108
- f.puts "#{@files[0]}:10"
109
- end
110
- end
111
-
112
112
  it "partitions by runtime when runtime-data is available" do
113
113
  klass.stub!(:puts)
114
114
  setup_runtime_log
115
115
 
116
- groups = klass.tests_in_groups(test_root, 2)
116
+ groups = klass.tests_in_groups([test_root], 2)
117
117
  groups.size.should == 2
118
118
  # 10 + 1 + 3 + 5 = 19
119
119
  groups[0].should == [@files[0],@files[1],@files[3],@files[5]]
@@ -125,7 +125,7 @@ def test_tests_in_groups(klass, folder, suffix)
125
125
  klass.stub!(:puts)
126
126
  setup_runtime_log
127
127
 
128
- groups = klass.tests_in_groups(test_root, 2)
128
+ groups = klass.tests_in_groups([test_root], 2)
129
129
  groups.size.should == 2
130
130
 
131
131
  groups[0].should == groups[0].sort
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0.alpha
4
+ version: 0.7.0.alpha2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-25 00:00:00.000000000 Z
12
+ date: 2012-02-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parallel
16
- requirement: &16612760 !ruby/object:Gem::Requirement
16
+ requirement: &16386080 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *16612760
24
+ version_requirements: *16386080
25
25
  description:
26
26
  email: michael@grosser.it
27
27
  executables:
@@ -36,6 +36,7 @@ files:
36
36
  - Gemfile.lock
37
37
  - Rakefile
38
38
  - Readme.md
39
+ - ReadmeRails2.md
39
40
  - bin/parallel_cucumber
40
41
  - bin/parallel_spec
41
42
  - bin/parallel_test
@@ -80,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
80
81
  version: '0'
81
82
  segments:
82
83
  - 0
83
- hash: -20791927037817869
84
+ hash: -3495402516497603622
84
85
  required_rubygems_version: !ruby/object:Gem::Requirement
85
86
  none: false
86
87
  requirements:
@@ -92,5 +93,5 @@ rubyforge_project:
92
93
  rubygems_version: 1.8.15
93
94
  signing_key:
94
95
  specification_version: 3
95
- summary: Run tests / specs / features in parallel
96
+ summary: Run Test::Unit / RSpec / Cucumber in parallel
96
97
  test_files: []