parallel_tests 0.7.0.alpha → 0.7.0.alpha2

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.
@@ -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: []