vinted-parallel_tests 0.13.3 → 1.7.0.1

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Readme.md +86 -47
  3. data/bin/parallel_cucumber +5 -1
  4. data/bin/parallel_rspec +5 -1
  5. data/bin/parallel_spinach +9 -0
  6. data/bin/parallel_test +5 -1
  7. data/lib/parallel_tests.rb +15 -2
  8. data/lib/parallel_tests/cli.rb +115 -35
  9. data/lib/parallel_tests/cucumber/failures_logger.rb +9 -7
  10. data/lib/parallel_tests/cucumber/runner.rb +16 -77
  11. data/lib/parallel_tests/cucumber/scenario_line_logger.rb +52 -0
  12. data/lib/parallel_tests/cucumber/scenarios.rb +34 -0
  13. data/lib/parallel_tests/{cucumber → gherkin}/io.rb +1 -1
  14. data/lib/parallel_tests/{cucumber/gherkin_listener.rb → gherkin/listener.rb} +11 -6
  15. data/lib/parallel_tests/gherkin/runner.rb +116 -0
  16. data/lib/parallel_tests/{cucumber → gherkin}/runtime_logger.rb +2 -2
  17. data/lib/parallel_tests/grouper.rb +34 -17
  18. data/lib/parallel_tests/rspec/failures_logger.rb +22 -14
  19. data/lib/parallel_tests/rspec/logger_base.rb +5 -1
  20. data/lib/parallel_tests/rspec/runner.rb +5 -4
  21. data/lib/parallel_tests/rspec/runtime_logger.rb +10 -5
  22. data/lib/parallel_tests/rspec/summary_logger.rb +11 -11
  23. data/lib/parallel_tests/spinach/runner.rb +19 -0
  24. data/lib/parallel_tests/tasks.rb +41 -18
  25. data/lib/parallel_tests/test/runner.rb +80 -28
  26. data/lib/parallel_tests/test/runtime_logger.rb +86 -55
  27. data/lib/parallel_tests/version.rb +1 -1
  28. metadata +18 -35
  29. data/.gitignore +0 -2
  30. data/.rspec +0 -2
  31. data/.travis.yml +0 -6
  32. data/Gemfile +0 -8
  33. data/Gemfile.lock +0 -48
  34. data/Rakefile +0 -6
  35. data/ReadmeRails2.md +0 -48
  36. data/parallel_tests.gemspec +0 -14
  37. data/spec/integration_spec.rb +0 -285
  38. data/spec/parallel_tests/cli_spec.rb +0 -71
  39. data/spec/parallel_tests/cucumber/failure_logger_spec.rb +0 -43
  40. data/spec/parallel_tests/cucumber/gherkin_listener_spec.rb +0 -97
  41. data/spec/parallel_tests/cucumber/runner_spec.rb +0 -179
  42. data/spec/parallel_tests/grouper_spec.rb +0 -52
  43. data/spec/parallel_tests/rspec/failures_logger_spec.rb +0 -82
  44. data/spec/parallel_tests/rspec/runner_spec.rb +0 -187
  45. data/spec/parallel_tests/rspec/runtime_logger_spec.rb +0 -126
  46. data/spec/parallel_tests/rspec/summary_logger_spec.rb +0 -37
  47. data/spec/parallel_tests/tasks_spec.rb +0 -151
  48. data/spec/parallel_tests/test/runner_spec.rb +0 -413
  49. data/spec/parallel_tests/test/runtime_logger_spec.rb +0 -90
  50. data/spec/parallel_tests_spec.rb +0 -137
  51. data/spec/spec_helper.rb +0 -157
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 14191ef3ae581ab27537c66e193d840bda6cd543
4
- data.tar.gz: b8a86d1c3f2ca975d563c0c7531fe7871ac3a06c
3
+ metadata.gz: c24065e6ecc117e7e693090d95bf2233b28bb83b
4
+ data.tar.gz: 959293ff1bc3211d8ef3f302f79e82313a1f8f50
5
5
  SHA512:
6
- metadata.gz: fdcf4a6e6456c7c142ab7fe45ceb751886f6d124585f6a3e1cc8d46dcf09d418ef30c4b5ed6c3426a55b8f901921c3518198ba7ce57ab547e806cb73ca59cce5
7
- data.tar.gz: fc9842768d2028f2943e43d905028b2bf0f692682265eae17c0c9ff3ac080073454f8a9cfd7826518e1084c85f277f570a479f8d0da9261645609bc0a5184e35
6
+ metadata.gz: b9a7b68b7a4c0ee89c2d6b3ad829a19eda204d383b5322131af6cdc46c4befb8836719bf2711b80445b9832283570707057eaf856285f2fe655534cae32dfc74
7
+ data.tar.gz: 58642970a41d300b6de94a702ef7891a6151e50f6439501133fb93fe3185f2af587c946e030eef4161347b910e953b65b452e3317d38b7b1a472e062ca5202e4
data/Readme.md CHANGED
@@ -1,36 +1,28 @@
1
- Speedup Test::Unit + RSpec + Cucumber by running parallel on multiple CPUs (or cores).<br/>
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.
1
+ # parallel_tests
3
2
 
4
- [upgrading from 0.6 ?](https://github.com/grosser/parallel_tests/wiki/Upgrading-0.6.x-to-0.7.x)
3
+ [![Gem Version](https://badge.fury.io/rb/parallel_tests.svg)](https://rubygems.org/gems/parallel_tests)
4
+ [![Build Status](https://travis-ci.org/grosser/parallel_tests.svg)](https://travis-ci.org/grosser/parallel_tests/builds)
5
+
6
+ Speedup Test::Unit + RSpec + Cucumber + Spinach by running parallel on multiple CPU cores.<br/>
7
+ ParallelTests splits tests into even groups (by number of lines or runtime) and runs each group in a single process with its own database.
5
8
 
6
9
  Setup for Rails
7
10
  ===============
8
11
  [RailsCasts episode #413 Fast Tests](http://railscasts.com/episodes/413-fast-tests)
9
- [still using Rails 2?](https://github.com/grosser/parallel_tests/blob/master/ReadmeRails2.md)
10
12
 
11
13
  ### Install
12
- If you use RSpec: ensure you got >= 2.4
13
-
14
- As gem
15
-
16
- ```ruby
17
- # add to Gemfile
18
- gem "parallel_tests", :group => :development
19
- ```
20
- OR as plugin
21
-
22
- rails plugin install git://github.com/grosser/parallel_tests.git
23
14
 
24
15
  ```ruby
25
- # add to Gemfile
26
- gem "parallel", :group => :development
16
+ # Gemfile
17
+ gem "parallel_tests", group: :development
27
18
  ```
28
19
 
29
20
  ### Add to `config/database.yml`
21
+
30
22
  ParallelTests uses 1 database per test-process.
31
23
  <table>
32
24
  <tr><td>Process number</td><td>1</td><td>2</td><td>3</td></tr>
33
- <tr><td>`ENV['TEST_ENV_NUMBER']`</td><td>''</td><td>'2'</td><td>'3'</td></tr>
25
+ <tr><td>ENV['TEST_ENV_NUMBER']</td><td>''</td><td>'2'</td><td>'3'</td></tr>
34
26
  </table>
35
27
 
36
28
  ```yaml
@@ -48,6 +40,7 @@ test:
48
40
  rake parallel:test # Test::Unit
49
41
  rake parallel:spec # RSpec
50
42
  rake parallel:features # Cucumber
43
+ rake parallel:features-spinach # Spinach
51
44
 
52
45
  rake parallel:test[1] --> force 1 CPU --> 86 seconds
53
46
  rake parallel:test --> got 2 CPUs? --> 47 seconds
@@ -97,37 +90,44 @@ end
97
90
  Loggers
98
91
  ===================
99
92
 
100
- Even process runtimes
101
- -----------------
93
+ Even test group run-times
94
+ -------------------------
95
+
96
+ ### RSpec
102
97
 
103
- Log test runtime to give each process the same runtime.
98
+ Add the `RuntimeLogger` to log how long each test takes to run.
99
+ This log file will be loaded on the next test run, and the tests will be grouped
100
+ so that each process should finish around the same time.
104
101
 
105
102
  Rspec: Add to your `.rspec_parallel` (or `.rspec`) :
106
103
 
107
- If installed as plugin: -I vendor/plugins/parallel_tests/lib
108
104
  --format progress
109
105
  --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
110
106
 
111
- Test::Unit: Add to your `test_helper.rb`:
107
+ ### Test::Unit & Minitest 4/5
108
+
109
+ Add to your `test_helper.rb`:
112
110
  ```ruby
113
- require 'parallel_tests/test/runtime_logger'
111
+ require 'parallel_tests/test/runtime_logger' if ENV['RECORD_RUNTIME']
114
112
  ```
115
113
 
114
+ results will be logged to tmp/parallel_runtime_test.log when `RECORD_RUNTIME` is set,
115
+ so it is not always required or overwritten.
116
+
116
117
  RSpec: SummaryLogger
117
118
  --------------------
118
119
 
119
- This logger logs the test output without the different processes overwriting each other.
120
+ Log the test output without the different processes overwriting each other.
120
121
 
121
122
  Add the following to your `.rspec_parallel` (or `.rspec`) :
122
123
 
123
- If installed as plugin: -I vendor/plugins/parallel_tests/lib
124
124
  --format progress
125
125
  --format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log
126
126
 
127
127
  RSpec: FailuresLogger
128
128
  -----------------------
129
129
 
130
- This logger produces pasteable command-line snippets for each failed example.
130
+ Produce pasteable command-line snippets for each failed example.
131
131
 
132
132
  E.g.
133
133
 
@@ -135,14 +135,13 @@ E.g.
135
135
 
136
136
  Add the following to your `.rspec_parallel` (or `.rspec`) :
137
137
 
138
- If installed as plugin: -I vendor/plugins/parallel_tests/lib
139
138
  --format progress
140
139
  --format ParallelTests::RSpec::FailuresLogger --out tmp/failing_specs.log
141
140
 
142
141
  Cucumber: FailuresLogger
143
142
  -----------------------
144
143
 
145
- This logger logs failed cucumber scenarios to the specified file. The filename can be passed to cucumber, prefixed with '@' to rerun failures.
144
+ Log failed cucumber scenarios to the specified file. The filename can be passed to cucumber, prefixed with '@' to rerun failures.
146
145
 
147
146
  Usage:
148
147
 
@@ -152,47 +151,65 @@ Or add the formatter to the `parallel:` profile of your `cucumber.yml`:
152
151
 
153
152
  parallel: --format progress --format ParallelTests::Cucumber::FailuresLogger --out tmp/cucumber_failures.log
154
153
 
154
+ Note if your `cucumber.yml` default profile uses `<%= std_opts %>` you may need to insert this as follows `parallel: <%= std_opts %> --format progress...`
155
155
 
156
156
  To rerun failures:
157
157
 
158
- cucumber @tmp/cucumber_failures.log
158
+ cucumber @tmp/cucumber_failures.log
159
159
 
160
160
  Setup for non-rails
161
161
  ===================
162
+
162
163
  gem install parallel_tests
163
164
  # go to your project dir
164
165
  parallel_test test/
165
166
  parallel_rspec spec/
166
167
  parallel_cucumber features/
168
+ parallel_spinach features/
167
169
 
168
- - use ENV['TEST_ENV_NUMBER'] inside your tests to select separate db/memcache/etc.
170
+ - use `ENV['TEST_ENV_NUMBER']` inside your tests to select separate db/memcache/etc.
169
171
  - Only run selected files & folders:
170
172
 
171
- parallel_test test/bar test/baz/foo_text.rb
173
+ `parallel_test test/bar test/baz/foo_text.rb`
174
+
175
+ - Pass test-options and files via `--`:
176
+
177
+ `parallel_test -- -t acceptance -f progress -- spec/foo_spec.rb spec/acceptance`
172
178
 
173
179
  Options are:
180
+ <!-- copy output from bundle exec ./bin/parallel_test -h -->
174
181
 
175
182
  -n [PROCESSES] How many processes to use, default: available CPUs
176
183
  -p, --pattern [PATTERN] run tests matching this pattern
177
184
  --group-by [TYPE] group tests by:
178
185
  found - order of finding files
179
- steps - number of cucumber steps
180
- default - runtime or filesize
186
+ steps - number of cucumber/spinach steps
187
+ scenarios - individual cucumber scenarios
188
+ filesize - by size of the file
189
+ runtime - info from runtime log
190
+ default - runtime when runtime log is filled otherwise filesize
181
191
  -m, --multiply-processes [FLOAT] use given number as a multiplier of processes to run
182
192
  -s, --single [PATTERN] Run all matching files in the same process
183
193
  -i, --isolate Do not run any other tests in the group used by --single(-s)
184
- -e, --exec [COMMAND] execute this code parallel and with ENV['TEST_ENV_NUM']
194
+ --only-group INT[, INT]
195
+ -e, --exec [COMMAND] execute this code parallel and with ENV['TEST_ENV_NUMBER']
185
196
  -o, --test-options '[OPTIONS]' execute test commands with those options
186
- -t, --type [TYPE] test(default) / rspec / cucumber
197
+ -t, --type [TYPE] test(default) / rspec / cucumber / spinach
198
+ --suffix [PATTERN] override built in test file pattern (should match suffix):
199
+ '_spec.rb$' - matches rspec files
200
+ '_(test|spec).rb$' - matches test or spec files
187
201
  --serialize-stdout Serialize stdout output, nothing will be written until everything is done
202
+ --combine-stderr Combine stderr into stdout, useful in conjunction with --serialize-stdout
188
203
  --non-parallel execute same commands but do not in parallel, needs --exec
189
204
  --no-symlinks Do not traverse symbolic links to find test files
190
205
  --ignore-tags [PATTERN] When counting steps ignore scenarios with tags that match this pattern
191
206
  --nice execute test commands with low priority.
207
+ --runtime-log [PATH] Location of previously recorded test runtimes
208
+ --verbose Print more output
192
209
  -v, --version Show Version
193
210
  -h, --help Show this.
194
211
 
195
- You can run any kind of code in parallel with -e / --execute
212
+ You can run any kind of code in parallel with -e / --exec
196
213
 
197
214
  parallel_test -n 5 -e 'ruby -e "puts %[hello from process #{ENV[:TEST_ENV_NUMBER.to_s].inspect}]"'
198
215
  hello from process "2"
@@ -210,11 +227,10 @@ You can run any kind of code in parallel with -e / --execute
210
227
  TIPS
211
228
  ====
212
229
  - [RSpec] add a `.rspec_parallel` to use different options, e.g. **no --drb**
213
- - [RSpec] delete `script/spec`
214
- - [[Spork](https://github.com/sporkrb/spork)] does not work with parallel_tests
215
- - [RSpec] remove --loadby from you spec/*.opts
230
+ - Spring does not work with parallel_tests, use `DISABLE_SPRING=1 rake parallel:spec` if you have spring hardcoded in your binaries
231
+ - [RSpec] remove `--loadby` from `.rspec`
216
232
  - [RSpec] Instantly see failures (instead of just a red F) with [rspec-instafail](https://github.com/grosser/rspec-instafail)
217
- - [Bundler] if you have a `Gemfile` then `bundle exec` will be used to run tests
233
+ - [RSpec] use [rspec-retry](https://github.com/NoRedInk/rspec-retry) (not rspec-rerun) to rerun failed tests.
218
234
  - [Cucumber] add a `parallel: foo` profile to your `config/cucumber.yml` and it will be used to run parallel tests
219
235
  - [Capybara setup](https://github.com/grosser/parallel_tests/wiki)
220
236
  - [Sphinx setup](https://github.com/grosser/parallel_tests/wiki)
@@ -223,13 +239,16 @@ TIPS
223
239
  - `export PARALLEL_TEST_PROCESSORS=X` in your environment and parallel_tests will use this number of processors by default
224
240
  - [ZSH] use quotes to use rake arguments `rake "parallel:prepare[3]"`
225
241
  - [email_spec and/or action_mailer_cache_delivery](https://github.com/grosser/parallel_tests/wiki)
226
- - [Memcached] use different namespaces e.g. `config.cache_store = ..., :namespace => "test_#{ENV['TEST_ENV_NUMBER']}"`
242
+ - [Memcached] use different namespaces e.g. `config.cache_store = ..., namespace: "test_#{ENV['TEST_ENV_NUMBER']}"`
243
+ - [zeus-parallel_tests](https://github.com/sevos/zeus-parallel_tests)
244
+ - [Distributed parallel test (e.g. Travis Support)](https://github.com/grosser/parallel_tests/wiki/Distributed-Parallel-Tests-and-Travis-Support)
245
+ - Debug errors that only happen with multiple files using `--verbose` and [cleanser](https://github.com/grosser/cleanser)
246
+ - Shell alias: `alias prspec='parallel_rspec -m 2 --'`
247
+ - Contribute your own gotaches to the [Wiki](https://github.com/grosser/parallel_tests/wiki) or even better open a PR :)
227
248
 
228
249
  TODO
229
250
  ====
230
- - make tests consistently pass with `--order random` in .rspec
231
251
  - fix tests vs cucumber >= 1.2 `unknown option --format`
232
- - add integration tests for the rake tasks, maybe generate a rails project ...
233
252
  - add unit tests for cucumber runtime formatter
234
253
  - make windows compatible
235
254
 
@@ -237,7 +256,7 @@ Authors
237
256
  ====
238
257
  inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-parallelize-your-rspec-suite)
239
258
 
240
- ### [Contributors](http://github.com/grosser/parallel_tests/contributors)
259
+ ### [Contributors](https://github.com/grosser/parallel_tests/contributors)
241
260
  - [Charles Finkel](http://charlesfinkel.com/)
242
261
  - [Indrek Juhkam](http://urgas.eu)
243
262
  - [Jason Morrison](http://jayunit.net)
@@ -286,8 +305,28 @@ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-
286
305
  - [Iain Beeston](https://github.com/iainbeeston)
287
306
  - [Alejandro Pulver](https://github.com/alepulver)
288
307
  - [Felix Clack](https://github.com/felixclack)
308
+ - [Izaak Alpert](https://github.com/karlhungus)
309
+ - [Micah Geisel](https://github.com/botandrose)
310
+ - [Exoth](https://github.com/Exoth)
311
+ - [sidfarkus](https://github.com/sidfarkus)
312
+ - [Colin Harris](https://github.com/aberant)
313
+ - [Wataru MIYAGUNI](https://github.com/gongo)
314
+ - [Brandon Turner](https://github.com/blt04)
315
+ - [Matt Hodgson](https://github.com/mhodgson)
316
+ - [bicarbon8](https://github.com/bicarbon8)
317
+ - [seichner](https://github.com/seichner)
318
+ - [Matt Southerden](https://github.com/mattsoutherden)
319
+ - [Stanislaw Wozniak](https://github.com/sponte)
320
+ - [Dmitry Polushkin](https://github.com/dmitry)
321
+ - [Samer Masry](https://github.com/smasry)
322
+ - [Volodymyr Mykhailyk](https:/github.com/volodymyr-mykhailyk)
323
+ - [Mike Mueller](https://github.com/mmueller)
324
+ - [Aaron Jensen](https://github.com/aaronjensen)
325
+ - [Ed Slocomb](https://github.com/edslocomb)
326
+ - [Cezary Baginski](https://github.com/e2)
327
+ - [Marius Ioana](https://github.com/mariusioana)
328
+
289
329
 
290
330
  [Michael Grosser](http://grosser.it)<br/>
291
331
  michael@grosser.it<br/>
292
- License: MIT<br/>
293
- [![Build Status](https://travis-ci.org/grosser/parallel_tests.png)](https://travis-ci.org/grosser/parallel_tests)
332
+ License: MIT
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
- $LOAD_PATH << File.expand_path("../../lib", __FILE__)
2
+
3
+ # enable local usage from cloned repo
4
+ root = File.expand_path("../..", __FILE__)
5
+ $LOAD_PATH << "#{root}/lib" if File.exist?("#{root}/Gemfile")
6
+
3
7
  require "parallel_tests"
4
8
 
5
9
  ParallelTests::CLI.new.run(["--type", "cucumber"] + ARGV)
data/bin/parallel_rspec CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
- $LOAD_PATH << File.expand_path("../../lib", __FILE__)
2
+
3
+ # enable local usage from cloned repo
4
+ root = File.expand_path("../..", __FILE__)
5
+ $LOAD_PATH << "#{root}/lib" if File.exist?("#{root}/Gemfile")
6
+
3
7
  require "parallel_tests"
4
8
 
5
9
  ParallelTests::CLI.new.run(["--type", "rspec"] + ARGV)
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # enable local usage from cloned repo
4
+ root = File.expand_path("../..", __FILE__)
5
+ $LOAD_PATH << "#{root}/lib" if File.exist?("#{root}/Gemfile")
6
+
7
+ require "parallel_tests"
8
+
9
+ ParallelTests::CLI.new.run(["--type", "spinach"] + ARGV)
data/bin/parallel_test CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
- $LOAD_PATH << File.expand_path("../../lib", __FILE__)
2
+
3
+ # enable local usage from cloned repo
4
+ root = File.expand_path("../..", __FILE__)
5
+ $LOAD_PATH << "#{root}/lib" if File.exist?("#{root}/Gemfile")
6
+
3
7
  require "parallel_tests"
4
8
 
5
9
  ParallelTests::CLI.new.run(["--type", "test"] + ARGV)
@@ -1,8 +1,15 @@
1
1
  require "parallel"
2
2
  require "parallel_tests/railtie" if defined? Rails::Railtie
3
+ require "rbconfig"
3
4
 
4
5
  module ParallelTests
5
- GREP_PROCESSES_COMMAND = "ps -ef | grep [T]EST_ENV_NUMBER= 2>&1"
6
+ WINDOWS = (RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
7
+ GREP_PROCESSES_COMMAND = \
8
+ if WINDOWS
9
+ "wmic process get commandline | findstr TEST_ENV_NUMBER | find /c \"TEST_ENV_NUMBER=\" 2>&1"
10
+ else
11
+ "ps -ef | grep [T]EST_ENV_NUMBER= 2>&1"
12
+ end
6
13
 
7
14
  autoload :CLI, "parallel_tests/cli"
8
15
  autoload :VERSION, "parallel_tests/version"
@@ -26,7 +33,7 @@ module ParallelTests
26
33
 
27
34
  until !File.directory?(current) || current == previous
28
35
  filename = File.join(current, "Gemfile")
29
- return true if File.exists?(filename)
36
+ return true if File.exist?(filename)
30
37
  current, previous = File.expand_path("..", current), current
31
38
  end
32
39
 
@@ -57,5 +64,11 @@ module ParallelTests
57
64
  Time.now
58
65
  end
59
66
  end
67
+
68
+ def delta
69
+ before = now.to_f
70
+ yield
71
+ now.to_f - before
72
+ end
60
73
  end
61
74
  end
@@ -1,6 +1,7 @@
1
1
  require 'optparse'
2
2
  require 'tempfile'
3
3
  require 'parallel_tests'
4
+ require 'shellwords'
4
5
 
5
6
  module ParallelTests
6
7
  class CLI
@@ -23,7 +24,7 @@ module ParallelTests
23
24
  Tempfile.open 'parallel_tests-lock' do |lock|
24
25
  return Parallel.map(items, :in_threads => num_processes) do |item|
25
26
  result = yield(item)
26
- report_output(result, lock) if options[:serialize_stdout]
27
+ reprint_output(result, lock.path) if options[:serialize_stdout]
27
28
  result
28
29
  end
29
30
  end
@@ -34,10 +35,20 @@ module ParallelTests
34
35
 
35
36
  report_time_taken do
36
37
  groups = @runner.tests_in_groups(options[:files], num_processes, options)
37
- report_number_of_tests(groups)
38
+ groups.reject! &:empty?
38
39
 
39
- test_results = execute_in_parallel(groups, groups.size, options) do |group|
40
- run_tests(group, groups.index(group), num_processes, options)
40
+ test_results = if options[:only_group]
41
+ groups_to_run = options[:only_group].collect{|i| groups[i - 1]}.compact
42
+ report_number_of_tests(groups_to_run)
43
+ execute_in_parallel(groups_to_run, groups_to_run.size, options) do |group|
44
+ run_tests(group, groups_to_run.index(group), 1, options)
45
+ end
46
+ else
47
+ report_number_of_tests(groups)
48
+
49
+ execute_in_parallel(groups, groups.size, options) do |group|
50
+ run_tests(group, groups.index(group), num_processes, options)
51
+ end
41
52
  end
42
53
 
43
54
  report_results(test_results)
@@ -54,12 +65,23 @@ module ParallelTests
54
65
  end
55
66
  end
56
67
 
57
- def report_output(result, lock)
58
- lock.flock File::LOCK_EX
59
- $stdout.puts result[:stdout]
60
- $stdout.flush
61
- ensure
62
- lock.flock File::LOCK_UN
68
+ def reprint_output(result, lockfile)
69
+ lock(lockfile) do
70
+ $stdout.puts result[:stdout]
71
+ $stdout.flush
72
+ end
73
+ end
74
+
75
+ def lock(lockfile)
76
+ File.open(lockfile) do |lock|
77
+ begin
78
+ lock.flock File::LOCK_EX
79
+ yield
80
+ ensure
81
+ # This shouldn't be necessary, but appears to be
82
+ lock.flock File::LOCK_UN
83
+ end
84
+ end
63
85
  end
64
86
 
65
87
  def report_results(test_results)
@@ -71,8 +93,9 @@ module ParallelTests
71
93
  def report_number_of_tests(groups)
72
94
  name = @runner.test_file_name
73
95
  num_processes = groups.size
74
- num_tests = groups.map(&:size).inject(:+)
75
- puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
96
+ num_tests = groups.map(&:size).inject(0, :+)
97
+ tests_per_process = (num_processes == 0 ? 0 : num_tests / num_processes)
98
+ puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{tests_per_process} #{name}s per process"
76
99
  end
77
100
 
78
101
  #exit with correct status code so rake parallel:test && echo 123 works
@@ -83,23 +106,29 @@ module ParallelTests
83
106
  def parse_options!(argv)
84
107
  options = {}
85
108
  OptionParser.new do |opts|
86
- opts.banner = <<BANNER
87
- Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
109
+ opts.banner = <<-BANNER.gsub(/^ /, '')
110
+ Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
111
+
112
+ [optional] Only selected files & folders:
113
+ parallel_test test/bar test/baz/xxx_text.rb
88
114
 
89
- [optional] Only run selected files & folders:
90
- parallel_test test/bar test/baz/xxx_text.rb
115
+ [optional] Pass test-options and files via `--`:
116
+ parallel_test -- -t acceptance -f progress -- spec/foo_spec.rb spec/acceptance
91
117
 
92
- Options are:
93
- BANNER
118
+ Options are:
119
+ BANNER
94
120
  opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs") { |n| options[:count] = n }
95
121
  opts.on("-p", "--pattern [PATTERN]", "run tests matching this pattern") { |pattern| options[:pattern] = /#{pattern}/ }
96
- opts.on("--group-by [TYPE]", <<-TEXT
97
- group tests by:
98
- found - order of finding files
99
- steps - number of cucumber steps
100
- default - runtime or filesize
101
- TEXT
102
- ) { |type| options[:group_by] = type.to_sym }
122
+ opts.on("--group-by [TYPE]", <<-TEXT.gsub(/^ /, '')
123
+ group tests by:
124
+ found - order of finding files
125
+ steps - number of cucumber/spinach steps
126
+ scenarios - individual cucumber scenarios
127
+ filesize - by size of the file
128
+ runtime - info from runtime log
129
+ default - runtime when runtime log is filled otherwise filesize
130
+ TEXT
131
+ ) { |type| options[:group_by] = type.to_sym }
103
132
  opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run") { |multiply| options[:multiply] = multiply }
104
133
 
105
134
  opts.on("-s [PATTERN]", "--single [PATTERN]",
@@ -115,9 +144,11 @@ TEXT
115
144
  options[:isolate] = true
116
145
  end
117
146
 
118
- opts.on("-e", "--exec [COMMAND]", "execute this code parallel and with ENV['TEST_ENV_NUM']") { |path| options[:execute] = path }
119
- opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options") { |arg| options[:test_options] = arg }
120
- opts.on("-t", "--type [TYPE]", "test(default) / rspec / cucumber") do |type|
147
+ opts.on("--only-group INT[, INT]", Array) { |groups| options[:only_group] = groups.map(&:to_i) }
148
+
149
+ opts.on("-e", "--exec [COMMAND]", "execute this code parallel and with ENV['TEST_ENV_NUMBER']") { |path| options[:execute] = path }
150
+ opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options") { |arg| options[:test_options] = arg.lstrip }
151
+ opts.on("-t", "--type [TYPE]", "test(default) / rspec / cucumber / spinach") do |type|
121
152
  begin
122
153
  @runner = load_runner(type)
123
154
  rescue NameError, LoadError => e
@@ -125,30 +156,73 @@ TEXT
125
156
  abort
126
157
  end
127
158
  end
159
+ opts.on("--suffix [PATTERN]", <<-TEXT.gsub(/^ /, '')
160
+ override built in test file pattern (should match suffix):
161
+ '_spec\.rb$' - matches rspec files
162
+ '_(test|spec).rb$' - matches test or spec files
163
+ TEXT
164
+ ) { |pattern| options[:suffix] = /#{pattern}/ }
128
165
  opts.on("--serialize-stdout", "Serialize stdout output, nothing will be written until everything is done") { options[:serialize_stdout] = true }
166
+ opts.on("--combine-stderr", "Combine stderr into stdout, useful in conjunction with --serialize-stdout") { options[:combine_stderr] = true }
129
167
  opts.on("--non-parallel", "execute same commands but do not in parallel, needs --exec") { options[:non_parallel] = true }
130
168
  opts.on("--no-symlinks", "Do not traverse symbolic links to find test files") { options[:symlinks] = false }
131
169
  opts.on("--advance-number [NUMBER]", Integer, "Advance test env number by specified number") { |n| options[:advance_number] = n }
132
170
  opts.on('--ignore-tags [PATTERN]', 'When counting steps ignore scenarios with tags that match this pattern') { |arg| options[:ignore_tag_pattern] = arg }
133
171
  opts.on("--nice", "execute test commands with low priority.") { options[:nice] = true }
172
+ opts.on("--runtime-log [PATH]", "Location of previously recorded test runtimes") { |path| options[:runtime_log] = path }
173
+ opts.on("--unknown-runtime [FLOAT]", "Use given number as unknown runtime (otherwise use average time)") { |time| options[:unknown_runtime] = time.to_f }
174
+ opts.on("--verbose", "Print more output") { options[:verbose] = true }
134
175
  opts.on("-v", "--version", "Show Version") { puts ParallelTests::VERSION; exit }
135
176
  opts.on("-h", "--help", "Show this.") { puts opts; exit }
136
177
  end.parse!(argv)
137
178
 
138
- raise "--group-by found and --single-process are not supported" if options[:group_by] == :found and options[:single_process]
139
-
140
179
  if options[:count] == 0
141
180
  options.delete(:count)
142
181
  options[:non_parallel] = true
143
182
  end
144
183
 
145
- options[:files] = argv
184
+ files, remaining = extract_file_paths(argv)
185
+ unless options[:execute]
186
+ abort "Pass files or folders to run" unless files.any?
187
+ options[:files] = files
188
+ end
189
+
190
+ append_test_options(options, remaining)
191
+
192
+ options[:group_by] ||= :filesize if options[:only_group]
193
+
194
+ raise "--group-by found and --single-process are not supported" if options[:group_by] == :found and options[:single_process]
195
+ allowed = [:filesize, :runtime, :found]
196
+ if !allowed.include?(options[:group_by]) && options[:only_group]
197
+ raise "--group-by #{allowed.join(" or ")} is required for --only-group"
198
+ end
199
+
146
200
  options
147
201
  end
148
202
 
203
+ def extract_file_paths(argv)
204
+ dash_index = argv.rindex("--")
205
+ file_args_at = (dash_index || -1) + 1
206
+ [argv[file_args_at..-1], argv[0...(dash_index || 0)]]
207
+ end
208
+
209
+ def extract_test_options(argv)
210
+ dash_index = argv.index("--") || -1
211
+ argv[dash_index+1..-1]
212
+ end
213
+
214
+ def append_test_options(options, argv)
215
+ new_opts = extract_test_options(argv)
216
+ return if new_opts.empty?
217
+
218
+ prev_and_new = [options[:test_options], new_opts.shelljoin]
219
+ options[:test_options] = prev_and_new.compact.join(' ')
220
+ end
221
+
149
222
  def load_runner(type)
150
223
  require "parallel_tests/#{type}/runner"
151
- klass_name = "ParallelTests::#{type.capitalize.sub("Rspec", "RSpec")}::Runner"
224
+ runner_classname = type.split("_").map(&:capitalize).join.sub("Rspec", "RSpec")
225
+ klass_name = "ParallelTests::#{runner_classname}::Runner"
152
226
  klass_name.split('::').inject(Object) { |x, y| x.const_get(y) }
153
227
  end
154
228
 
@@ -168,9 +242,15 @@ TEXT
168
242
  end
169
243
 
170
244
  def report_time_taken
171
- start = Time.now
172
- yield
173
- puts "\nTook #{Time.now - start} seconds"
245
+ seconds = ParallelTests.delta { yield }.to_i
246
+ puts "\nTook #{seconds} seconds#{detailed_duration(seconds)}"
247
+ end
248
+
249
+ def detailed_duration(seconds)
250
+ parts = [ seconds / 3600, seconds % 3600 / 60, seconds % 60 ].drop_while(&:zero?)
251
+ return if parts.size < 2
252
+ parts = parts.map { |i| "%02d" % i }.join(':').sub(/^0/, '')
253
+ " (#{parts})"
174
254
  end
175
255
 
176
256
  def final_fail_message