vinted-parallel_tests 0.13.3 → 1.7.0.1

Sign up to get free protection for your applications and to get access to all the features.
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