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.
- checksums.yaml +4 -4
- data/Readme.md +86 -47
- data/bin/parallel_cucumber +5 -1
- data/bin/parallel_rspec +5 -1
- data/bin/parallel_spinach +9 -0
- data/bin/parallel_test +5 -1
- data/lib/parallel_tests.rb +15 -2
- data/lib/parallel_tests/cli.rb +115 -35
- data/lib/parallel_tests/cucumber/failures_logger.rb +9 -7
- data/lib/parallel_tests/cucumber/runner.rb +16 -77
- data/lib/parallel_tests/cucumber/scenario_line_logger.rb +52 -0
- data/lib/parallel_tests/cucumber/scenarios.rb +34 -0
- data/lib/parallel_tests/{cucumber → gherkin}/io.rb +1 -1
- data/lib/parallel_tests/{cucumber/gherkin_listener.rb → gherkin/listener.rb} +11 -6
- data/lib/parallel_tests/gherkin/runner.rb +116 -0
- data/lib/parallel_tests/{cucumber → gherkin}/runtime_logger.rb +2 -2
- data/lib/parallel_tests/grouper.rb +34 -17
- data/lib/parallel_tests/rspec/failures_logger.rb +22 -14
- data/lib/parallel_tests/rspec/logger_base.rb +5 -1
- data/lib/parallel_tests/rspec/runner.rb +5 -4
- data/lib/parallel_tests/rspec/runtime_logger.rb +10 -5
- data/lib/parallel_tests/rspec/summary_logger.rb +11 -11
- data/lib/parallel_tests/spinach/runner.rb +19 -0
- data/lib/parallel_tests/tasks.rb +41 -18
- data/lib/parallel_tests/test/runner.rb +80 -28
- data/lib/parallel_tests/test/runtime_logger.rb +86 -55
- data/lib/parallel_tests/version.rb +1 -1
- metadata +18 -35
- data/.gitignore +0 -2
- data/.rspec +0 -2
- data/.travis.yml +0 -6
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -48
- data/Rakefile +0 -6
- data/ReadmeRails2.md +0 -48
- data/parallel_tests.gemspec +0 -14
- data/spec/integration_spec.rb +0 -285
- data/spec/parallel_tests/cli_spec.rb +0 -71
- data/spec/parallel_tests/cucumber/failure_logger_spec.rb +0 -43
- data/spec/parallel_tests/cucumber/gherkin_listener_spec.rb +0 -97
- data/spec/parallel_tests/cucumber/runner_spec.rb +0 -179
- data/spec/parallel_tests/grouper_spec.rb +0 -52
- data/spec/parallel_tests/rspec/failures_logger_spec.rb +0 -82
- data/spec/parallel_tests/rspec/runner_spec.rb +0 -187
- data/spec/parallel_tests/rspec/runtime_logger_spec.rb +0 -126
- data/spec/parallel_tests/rspec/summary_logger_spec.rb +0 -37
- data/spec/parallel_tests/tasks_spec.rb +0 -151
- data/spec/parallel_tests/test/runner_spec.rb +0 -413
- data/spec/parallel_tests/test/runtime_logger_spec.rb +0 -90
- data/spec/parallel_tests_spec.rb +0 -137
- data/spec/spec_helper.rb +0 -157
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c24065e6ecc117e7e693090d95bf2233b28bb83b
|
4
|
+
data.tar.gz: 959293ff1bc3211d8ef3f302f79e82313a1f8f50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9a7b68b7a4c0ee89c2d6b3ad829a19eda204d383b5322131af6cdc46c4befb8836719bf2711b80445b9832283570707057eaf856285f2fe655534cae32dfc74
|
7
|
+
data.tar.gz: 58642970a41d300b6de94a702ef7891a6151e50f6439501133fb93fe3185f2af587c946e030eef4161347b910e953b65b452e3317d38b7b1a472e062ca5202e4
|
data/Readme.md
CHANGED
@@ -1,36 +1,28 @@
|
|
1
|
-
|
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
|
-
[
|
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
|
-
#
|
26
|
-
gem "
|
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
|
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
|
101
|
-
|
93
|
+
Even test group run-times
|
94
|
+
-------------------------
|
95
|
+
|
96
|
+
### RSpec
|
102
97
|
|
103
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 / --
|
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
|
-
-
|
214
|
-
- [
|
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
|
-
- [
|
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 = ..., :
|
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](
|
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
|
293
|
-
[![Build Status](https://travis-ci.org/grosser/parallel_tests.png)](https://travis-ci.org/grosser/parallel_tests)
|
332
|
+
License: MIT
|
data/bin/parallel_cucumber
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
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
|
-
|
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)
|
data/bin/parallel_test
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
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)
|
data/lib/parallel_tests.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
data/lib/parallel_tests/cli.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
38
|
+
groups.reject! &:empty?
|
38
39
|
|
39
|
-
test_results =
|
40
|
-
|
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
|
58
|
-
lock
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
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 =
|
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]
|
90
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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("-
|
119
|
-
|
120
|
-
opts.on("-
|
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
|
-
|
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
|
-
|
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
|
-
|
172
|
-
|
173
|
-
|
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
|