vinted-parallel_tests 0.13.3

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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +48 -0
  7. data/Rakefile +6 -0
  8. data/Readme.md +293 -0
  9. data/ReadmeRails2.md +48 -0
  10. data/bin/parallel_cucumber +5 -0
  11. data/bin/parallel_rspec +5 -0
  12. data/bin/parallel_test +5 -0
  13. data/lib/parallel_tests/cli.rb +187 -0
  14. data/lib/parallel_tests/cucumber/failures_logger.rb +25 -0
  15. data/lib/parallel_tests/cucumber/gherkin_listener.rb +82 -0
  16. data/lib/parallel_tests/cucumber/io.rb +41 -0
  17. data/lib/parallel_tests/cucumber/runner.rb +98 -0
  18. data/lib/parallel_tests/cucumber/runtime_logger.rb +28 -0
  19. data/lib/parallel_tests/grouper.rb +56 -0
  20. data/lib/parallel_tests/railtie.rb +8 -0
  21. data/lib/parallel_tests/rspec/failures_logger.rb +44 -0
  22. data/lib/parallel_tests/rspec/logger_base.rb +52 -0
  23. data/lib/parallel_tests/rspec/runner.rb +72 -0
  24. data/lib/parallel_tests/rspec/runtime_logger.rb +54 -0
  25. data/lib/parallel_tests/rspec/summary_logger.rb +19 -0
  26. data/lib/parallel_tests/tasks.rb +139 -0
  27. data/lib/parallel_tests/test/runner.rb +168 -0
  28. data/lib/parallel_tests/test/runtime_logger.rb +97 -0
  29. data/lib/parallel_tests/version.rb +3 -0
  30. data/lib/parallel_tests.rb +61 -0
  31. data/parallel_tests.gemspec +14 -0
  32. data/spec/integration_spec.rb +285 -0
  33. data/spec/parallel_tests/cli_spec.rb +71 -0
  34. data/spec/parallel_tests/cucumber/failure_logger_spec.rb +43 -0
  35. data/spec/parallel_tests/cucumber/gherkin_listener_spec.rb +97 -0
  36. data/spec/parallel_tests/cucumber/runner_spec.rb +179 -0
  37. data/spec/parallel_tests/grouper_spec.rb +52 -0
  38. data/spec/parallel_tests/rspec/failures_logger_spec.rb +82 -0
  39. data/spec/parallel_tests/rspec/runner_spec.rb +187 -0
  40. data/spec/parallel_tests/rspec/runtime_logger_spec.rb +126 -0
  41. data/spec/parallel_tests/rspec/summary_logger_spec.rb +37 -0
  42. data/spec/parallel_tests/tasks_spec.rb +151 -0
  43. data/spec/parallel_tests/test/runner_spec.rb +413 -0
  44. data/spec/parallel_tests/test/runtime_logger_spec.rb +90 -0
  45. data/spec/parallel_tests_spec.rb +137 -0
  46. data/spec/spec_helper.rb +157 -0
  47. metadata +110 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 14191ef3ae581ab27537c66e193d840bda6cd543
4
+ data.tar.gz: b8a86d1c3f2ca975d563c0c7531fe7871ac3a06c
5
+ SHA512:
6
+ metadata.gz: fdcf4a6e6456c7c142ab7fe45ceb751886f6d124585f6a3e1cc8d46dcf09d418ef30c4b5ed6c3426a55b8f901921c3518198ba7ce57ab547e806cb73ca59cce5
7
+ data.tar.gz: fc9842768d2028f2943e43d905028b2bf0f692682265eae17c0c9ff3ac080073454f8a9cfd7826518e1084c85f277f570a479f8d0da9261645609bc0a5184e35
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.sh
2
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --backtrace
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - ree
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - jruby-1.7.3
6
+ - jruby-head
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'bump'
5
+ gem 'test-unit'
6
+ gem 'rspec', '>=2.4'
7
+ gem 'cucumber'
8
+ gem 'rake'
data/Gemfile.lock ADDED
@@ -0,0 +1,48 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ parallel_tests (0.13.3)
5
+ parallel
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ builder (3.0.0)
11
+ bump (0.3.8)
12
+ cucumber (1.1.4)
13
+ builder (>= 2.1.2)
14
+ diff-lcs (>= 1.1.2)
15
+ gherkin (~> 2.7.1)
16
+ json (>= 1.4.6)
17
+ term-ansicolor (>= 1.0.6)
18
+ diff-lcs (1.2.4)
19
+ gherkin (2.7.6)
20
+ json (>= 1.4.6)
21
+ gherkin (2.7.6-java)
22
+ json (>= 1.4.6)
23
+ json (1.7.5)
24
+ json (1.7.5-java)
25
+ parallel (0.6.5)
26
+ rake (10.0.3)
27
+ rspec (2.13.0)
28
+ rspec-core (~> 2.13.0)
29
+ rspec-expectations (~> 2.13.0)
30
+ rspec-mocks (~> 2.13.0)
31
+ rspec-core (2.13.1)
32
+ rspec-expectations (2.13.0)
33
+ diff-lcs (>= 1.1.3, < 2.0)
34
+ rspec-mocks (2.13.1)
35
+ term-ansicolor (1.0.7)
36
+ test-unit (2.4.4)
37
+
38
+ PLATFORMS
39
+ java
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ bump
44
+ cucumber
45
+ parallel_tests!
46
+ rake
47
+ rspec (>= 2.4)
48
+ test-unit
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bump/tasks'
2
+ require 'bundler/gem_tasks'
3
+
4
+ task :default do
5
+ sh "rspec spec/"
6
+ end
data/Readme.md ADDED
@@ -0,0 +1,293 @@
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.
3
+
4
+ [upgrading from 0.6 ?](https://github.com/grosser/parallel_tests/wiki/Upgrading-0.6.x-to-0.7.x)
5
+
6
+ Setup for Rails
7
+ ===============
8
+ [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
+
11
+ ### 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
+
24
+ ```ruby
25
+ # add to Gemfile
26
+ gem "parallel", :group => :development
27
+ ```
28
+
29
+ ### Add to `config/database.yml`
30
+ ParallelTests uses 1 database per test-process.
31
+ <table>
32
+ <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>
34
+ </table>
35
+
36
+ ```yaml
37
+ test:
38
+ database: yourproject_test<%= ENV['TEST_ENV_NUMBER'] %>
39
+ ```
40
+
41
+ ### Create additional database(s)
42
+ rake parallel:create
43
+
44
+ ### Copy development schema (repeat after migrations)
45
+ rake parallel:prepare
46
+
47
+ ### Run!
48
+ rake parallel:test # Test::Unit
49
+ rake parallel:spec # RSpec
50
+ rake parallel:features # Cucumber
51
+
52
+ rake parallel:test[1] --> force 1 CPU --> 86 seconds
53
+ rake parallel:test --> got 2 CPUs? --> 47 seconds
54
+ rake parallel:test --> got 4 CPUs? --> 26 seconds
55
+ ...
56
+
57
+ Test by pattern (e.g. use one integration server per subfolder / see if you broke any 'user'-related tests)
58
+
59
+ rake parallel:test[^test/unit] # every test file in test/unit folder
60
+ rake parallel:test[user] # run users_controller + user_helper + user tests
61
+ rake parallel:test['user|product'] # run user and product related tests
62
+
63
+
64
+ ### Example output
65
+
66
+ 2 processes for 210 specs, ~ 105 specs per process
67
+ ... test output ...
68
+
69
+ 843 examples, 0 failures, 1 pending
70
+
71
+ Took 29.925333 seconds
72
+
73
+ ### Run an arbitrary task in parallel
74
+ ```Bash
75
+ RAILS_ENV=test parallel_test -e "rake my:custom:task"
76
+ # or
77
+ rake parallel:rake[my:custom:task]
78
+ ```
79
+
80
+
81
+ Running things once
82
+ ===================
83
+
84
+ ```Ruby
85
+ # effected by race-condition: first process may boot slower the second
86
+ # either sleep a bit or use a lock for example File.lock
87
+ ParallelTests.first_process? ? do_something : sleep(1)
88
+
89
+ at_exit do
90
+ if ParallelTests.first_process?
91
+ ParallelTests.wait_for_other_processes_to_finish
92
+ undo_something
93
+ end
94
+ end
95
+ ```
96
+
97
+ Loggers
98
+ ===================
99
+
100
+ Even process runtimes
101
+ -----------------
102
+
103
+ Log test runtime to give each process the same runtime.
104
+
105
+ Rspec: Add to your `.rspec_parallel` (or `.rspec`) :
106
+
107
+ If installed as plugin: -I vendor/plugins/parallel_tests/lib
108
+ --format progress
109
+ --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
110
+
111
+ Test::Unit: Add to your `test_helper.rb`:
112
+ ```ruby
113
+ require 'parallel_tests/test/runtime_logger'
114
+ ```
115
+
116
+ RSpec: SummaryLogger
117
+ --------------------
118
+
119
+ This logger logs the test output without the different processes overwriting each other.
120
+
121
+ Add the following to your `.rspec_parallel` (or `.rspec`) :
122
+
123
+ If installed as plugin: -I vendor/plugins/parallel_tests/lib
124
+ --format progress
125
+ --format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log
126
+
127
+ RSpec: FailuresLogger
128
+ -----------------------
129
+
130
+ This logger produces pasteable command-line snippets for each failed example.
131
+
132
+ E.g.
133
+
134
+ rspec /path/to/my_spec.rb:123 # should do something
135
+
136
+ Add the following to your `.rspec_parallel` (or `.rspec`) :
137
+
138
+ If installed as plugin: -I vendor/plugins/parallel_tests/lib
139
+ --format progress
140
+ --format ParallelTests::RSpec::FailuresLogger --out tmp/failing_specs.log
141
+
142
+ Cucumber: FailuresLogger
143
+ -----------------------
144
+
145
+ This logger logs failed cucumber scenarios to the specified file. The filename can be passed to cucumber, prefixed with '@' to rerun failures.
146
+
147
+ Usage:
148
+
149
+ cucumber --format ParallelTests::Cucumber::FailuresLogger --out tmp/cucumber_failures.log
150
+
151
+ Or add the formatter to the `parallel:` profile of your `cucumber.yml`:
152
+
153
+ parallel: --format progress --format ParallelTests::Cucumber::FailuresLogger --out tmp/cucumber_failures.log
154
+
155
+
156
+ To rerun failures:
157
+
158
+ cucumber @tmp/cucumber_failures.log
159
+
160
+ Setup for non-rails
161
+ ===================
162
+ gem install parallel_tests
163
+ # go to your project dir
164
+ parallel_test test/
165
+ parallel_rspec spec/
166
+ parallel_cucumber features/
167
+
168
+ - use ENV['TEST_ENV_NUMBER'] inside your tests to select separate db/memcache/etc.
169
+ - Only run selected files & folders:
170
+
171
+ parallel_test test/bar test/baz/foo_text.rb
172
+
173
+ Options are:
174
+
175
+ -n [PROCESSES] How many processes to use, default: available CPUs
176
+ -p, --pattern [PATTERN] run tests matching this pattern
177
+ --group-by [TYPE] group tests by:
178
+ found - order of finding files
179
+ steps - number of cucumber steps
180
+ default - runtime or filesize
181
+ -m, --multiply-processes [FLOAT] use given number as a multiplier of processes to run
182
+ -s, --single [PATTERN] Run all matching files in the same process
183
+ -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']
185
+ -o, --test-options '[OPTIONS]' execute test commands with those options
186
+ -t, --type [TYPE] test(default) / rspec / cucumber
187
+ --serialize-stdout Serialize stdout output, nothing will be written until everything is done
188
+ --non-parallel execute same commands but do not in parallel, needs --exec
189
+ --no-symlinks Do not traverse symbolic links to find test files
190
+ --ignore-tags [PATTERN] When counting steps ignore scenarios with tags that match this pattern
191
+ --nice execute test commands with low priority.
192
+ -v, --version Show Version
193
+ -h, --help Show this.
194
+
195
+ You can run any kind of code in parallel with -e / --execute
196
+
197
+ parallel_test -n 5 -e 'ruby -e "puts %[hello from process #{ENV[:TEST_ENV_NUMBER.to_s].inspect}]"'
198
+ hello from process "2"
199
+ hello from process ""
200
+ hello from process "3"
201
+ hello from process "5"
202
+ hello from process "4"
203
+
204
+ <table>
205
+ <tr><td></td><td>1 Process</td><td>2 Processes</td><td>4 Processes</td></tr>
206
+ <tr><td>RSpec spec-suite</td><td>18s</td><td>14s</td><td>10s</td></tr>
207
+ <tr><td>Rails-ActionPack</td><td>88s</td><td>53s</td><td>44s</td></tr>
208
+ </table>
209
+
210
+ TIPS
211
+ ====
212
+ - [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
216
+ - [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
218
+ - [Cucumber] add a `parallel: foo` profile to your `config/cucumber.yml` and it will be used to run parallel tests
219
+ - [Capybara setup](https://github.com/grosser/parallel_tests/wiki)
220
+ - [Sphinx setup](https://github.com/grosser/parallel_tests/wiki)
221
+ - [Capistrano setup](https://github.com/grosser/parallel_tests/wiki/Remotely-with-capistrano) let your tests run on a big box instead of your laptop
222
+ - [SQL schema format] use :ruby schema format to get faster parallel:prepare`
223
+ - `export PARALLEL_TEST_PROCESSORS=X` in your environment and parallel_tests will use this number of processors by default
224
+ - [ZSH] use quotes to use rake arguments `rake "parallel:prepare[3]"`
225
+ - [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']}"`
227
+
228
+ TODO
229
+ ====
230
+ - make tests consistently pass with `--order random` in .rspec
231
+ - fix tests vs cucumber >= 1.2 `unknown option --format`
232
+ - add integration tests for the rake tasks, maybe generate a rails project ...
233
+ - add unit tests for cucumber runtime formatter
234
+ - make windows compatible
235
+
236
+ Authors
237
+ ====
238
+ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-parallelize-your-rspec-suite)
239
+
240
+ ### [Contributors](http://github.com/grosser/parallel_tests/contributors)
241
+ - [Charles Finkel](http://charlesfinkel.com/)
242
+ - [Indrek Juhkam](http://urgas.eu)
243
+ - [Jason Morrison](http://jayunit.net)
244
+ - [jinzhu](http://github.com/jinzhu)
245
+ - [Joakim Kolsjö](http://www.rubyblocks.se)
246
+ - [Kevin Scaldeferri](http://kevin.scaldeferri.com/blog/)
247
+ - [Kpumuk](http://kpumuk.info/)
248
+ - [Maksim Horbul](http://github.com/mhorbul)
249
+ - [Pivotal Labs](http://www.pivotallabs.com)
250
+ - [Rohan Deshpande](http://github.com/rdeshpande)
251
+ - [Tchandy](http://thiagopradi.net/)
252
+ - [Terence Lee](http://hone.heroku.com/)
253
+ - [Will Bryant](http://willbryant.net/)
254
+ - [Fred Wu](http://fredwu.me)
255
+ - [xxx](https://github.com/xxx)
256
+ - [Levent Ali](http://purebreeze.com/)
257
+ - [Michael Kintzer](https://github.com/rockrep)
258
+ - [nathansobo](https://github.com/nathansobo)
259
+ - [Joe Yates](http://titusd.co.uk)
260
+ - [asmega](http://www.ph-lee.com)
261
+ - [Doug Barth](https://github.com/dougbarth)
262
+ - [Geoffrey Hichborn](https://github.com/phene)
263
+ - [Trae Robrock](https://github.com/trobrock)
264
+ - [Lawrence Wang](https://github.com/levity)
265
+ - [Sean Walbran](https://github.com/seanwalbran)
266
+ - [Lawrence Wang](https://github.com/levity)
267
+ - [Potapov Sergey](https://github.com/greyblake)
268
+ - [Łukasz Tackowiak](https://github.com/lukasztackowiak)
269
+ - [Pedro Carriço](https://github.com/pedrocarrico)
270
+ - [Pablo Manrubia Díez](https://github.com/pmanrubia)
271
+ - [Slawomir Smiechura](https://github.com/ssmiech)
272
+ - [Georg Friedrich](https://github.com/georg)
273
+ - [R. Tyler Croy](https://github.com/rtyler)
274
+ - [Ulrich Berkmüller](https://github.com/ulrich-berkmueller)
275
+ - [Grzegorz Derebecki](https://github.com/madmax)
276
+ - [Florian Motlik](https://github.com/flomotlik)
277
+ - [Artem Kuzko](https://github.com/akuzko)
278
+ - [Zeke Fast](https://github.com/zekefast)
279
+ - [Joseph Shraibman](https://github.com/jshraibman-mdsol)
280
+ - [David Davis](https://github.com/daviddavis)
281
+ - [Ari Pollak](https://github.com/aripollak)
282
+ - [Aaron Jensen](https://github.com/aaronjensen)
283
+ - [Artur Roszczyk](https://github.com/sevos)
284
+ - [Caleb Tomlinson](https://github.com/calebTomlinson)
285
+ - [Jawwad Ahmad](https://github.com/jawwad)
286
+ - [Iain Beeston](https://github.com/iainbeeston)
287
+ - [Alejandro Pulver](https://github.com/alepulver)
288
+ - [Felix Clack](https://github.com/felixclack)
289
+
290
+ [Michael Grosser](http://grosser.it)<br/>
291
+ 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)
data/ReadmeRails2.md ADDED
@@ -0,0 +1,48 @@
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
+ # add to Rakefile
23
+ begin; require 'vendor/plugins/parallel_tests/lib/parallel_tests/tasks'; rescue LoadError; end
24
+
25
+
26
+ Even process runtimes
27
+ -----------------
28
+
29
+ RSpec 1.x:
30
+ --format progress
31
+ --require parallel_tests/rspec/runtime_logger
32
+ --format ParallelTests::RSpec::RuntimeLogger:tmp/parallel_runtime_rspec.log
33
+
34
+ SpecSummaryLogger
35
+ --------------------
36
+
37
+ RSpec 1.x:
38
+ --format progress
39
+ --require parallel_tests/rspec/summary_logger
40
+ --format ParallelTests::RSpec::SummaryLogger:tmp/spec_summary.log
41
+
42
+ SpecFailuresLogger
43
+ -----------------------
44
+
45
+ RSpec 1.x:
46
+ --format progress
47
+ --require parallel_tests/rspec/failures_logger
48
+ --format ParallelTests::RSpec::FailuresLogger:tmp/failing_specs.log
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
3
+ require "parallel_tests"
4
+
5
+ ParallelTests::CLI.new.run(["--type", "cucumber"] + ARGV)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
3
+ require "parallel_tests"
4
+
5
+ ParallelTests::CLI.new.run(["--type", "rspec"] + ARGV)
data/bin/parallel_test ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
3
+ require "parallel_tests"
4
+
5
+ ParallelTests::CLI.new.run(["--type", "test"] + ARGV)
@@ -0,0 +1,187 @@
1
+ require 'optparse'
2
+ require 'tempfile'
3
+ require 'parallel_tests'
4
+
5
+ module ParallelTests
6
+ class CLI
7
+ def run(argv)
8
+ options = parse_options!(argv)
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
+ run_tests_in_parallel(num_processes, options)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def execute_in_parallel(items, num_processes, options)
23
+ Tempfile.open 'parallel_tests-lock' do |lock|
24
+ return Parallel.map(items, :in_threads => num_processes) do |item|
25
+ result = yield(item)
26
+ report_output(result, lock) if options[:serialize_stdout]
27
+ result
28
+ end
29
+ end
30
+ end
31
+
32
+ def run_tests_in_parallel(num_processes, options)
33
+ test_results = nil
34
+
35
+ report_time_taken do
36
+ groups = @runner.tests_in_groups(options[:files], num_processes, options)
37
+ report_number_of_tests(groups)
38
+
39
+ test_results = execute_in_parallel(groups, groups.size, options) do |group|
40
+ run_tests(group, groups.index(group), num_processes, options)
41
+ end
42
+
43
+ report_results(test_results)
44
+ end
45
+
46
+ abort final_fail_message if any_test_failed?(test_results)
47
+ end
48
+
49
+ def run_tests(group, process_number, num_processes, options)
50
+ if group.empty?
51
+ {:stdout => '', :exit_status => 0}
52
+ else
53
+ @runner.run_tests(group, process_number, num_processes, options)
54
+ end
55
+ end
56
+
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
63
+ end
64
+
65
+ def report_results(test_results)
66
+ results = @runner.find_results(test_results.map { |result| result[:stdout] }*"")
67
+ puts ""
68
+ puts @runner.summarize_results(results)
69
+ end
70
+
71
+ def report_number_of_tests(groups)
72
+ name = @runner.test_file_name
73
+ 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"
76
+ end
77
+
78
+ #exit with correct status code so rake parallel:test && echo 123 works
79
+ def any_test_failed?(test_results)
80
+ test_results.any? { |result| result[:exit_status] != 0 }
81
+ end
82
+
83
+ def parse_options!(argv)
84
+ options = {}
85
+ OptionParser.new do |opts|
86
+ opts.banner = <<BANNER
87
+ Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
88
+
89
+ [optional] Only run selected files & folders:
90
+ parallel_test test/bar test/baz/xxx_text.rb
91
+
92
+ Options are:
93
+ BANNER
94
+ opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs") { |n| options[:count] = n }
95
+ 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 }
103
+ opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run") { |multiply| options[:multiply] = multiply }
104
+
105
+ opts.on("-s [PATTERN]", "--single [PATTERN]",
106
+ "Run all matching files in the same process") do |pattern|
107
+
108
+ options[:single_process] ||= []
109
+ options[:single_process] << /#{pattern}/
110
+ end
111
+
112
+ opts.on("-i", "--isolate",
113
+ "Do not run any other tests in the group used by --single(-s)") do |pattern|
114
+
115
+ options[:isolate] = true
116
+ end
117
+
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|
121
+ begin
122
+ @runner = load_runner(type)
123
+ rescue NameError, LoadError => e
124
+ puts "Runner for `#{type}` type has not been found! (#{e})"
125
+ abort
126
+ end
127
+ end
128
+ opts.on("--serialize-stdout", "Serialize stdout output, nothing will be written until everything is done") { options[:serialize_stdout] = true }
129
+ opts.on("--non-parallel", "execute same commands but do not in parallel, needs --exec") { options[:non_parallel] = true }
130
+ opts.on("--no-symlinks", "Do not traverse symbolic links to find test files") { options[:symlinks] = false }
131
+ opts.on("--advance-number [NUMBER]", Integer, "Advance test env number by specified number") { |n| options[:advance_number] = n }
132
+ opts.on('--ignore-tags [PATTERN]', 'When counting steps ignore scenarios with tags that match this pattern') { |arg| options[:ignore_tag_pattern] = arg }
133
+ opts.on("--nice", "execute test commands with low priority.") { options[:nice] = true }
134
+ opts.on("-v", "--version", "Show Version") { puts ParallelTests::VERSION; exit }
135
+ opts.on("-h", "--help", "Show this.") { puts opts; exit }
136
+ end.parse!(argv)
137
+
138
+ raise "--group-by found and --single-process are not supported" if options[:group_by] == :found and options[:single_process]
139
+
140
+ if options[:count] == 0
141
+ options.delete(:count)
142
+ options[:non_parallel] = true
143
+ end
144
+
145
+ options[:files] = argv
146
+ options
147
+ end
148
+
149
+ def load_runner(type)
150
+ require "parallel_tests/#{type}/runner"
151
+ klass_name = "ParallelTests::#{type.capitalize.sub("Rspec", "RSpec")}::Runner"
152
+ klass_name.split('::').inject(Object) { |x, y| x.const_get(y) }
153
+ end
154
+
155
+ def execute_shell_command_in_parallel(command, num_processes, options)
156
+ runs = (0...num_processes).to_a
157
+ results = if options[:non_parallel]
158
+ runs.map do |i|
159
+ ParallelTests::Test::Runner.execute_command(command, i, num_processes, options)
160
+ end
161
+ else
162
+ execute_in_parallel(runs, num_processes, options) do |i|
163
+ ParallelTests::Test::Runner.execute_command(command, i, num_processes, options)
164
+ end
165
+ end.flatten
166
+
167
+ abort if results.any? { |r| r[:exit_status] != 0 }
168
+ end
169
+
170
+ def report_time_taken
171
+ start = Time.now
172
+ yield
173
+ puts "\nTook #{Time.now - start} seconds"
174
+ end
175
+
176
+ def final_fail_message
177
+ fail_message = "#{@runner.name}s Failed"
178
+ fail_message = "\e[31m#{fail_message}\e[0m" if use_colors?
179
+
180
+ fail_message
181
+ end
182
+
183
+ def use_colors?
184
+ $stdout.tty?
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,25 @@
1
+ require 'cucumber/formatter/rerun'
2
+ require 'parallel_tests/cucumber/io'
3
+
4
+ module ParallelTests
5
+ module Cucumber
6
+ class FailuresLogger < ::Cucumber::Formatter::Rerun
7
+ include Io
8
+
9
+ def initialize(runtime, path_or_io, options)
10
+ @io = prepare_io(path_or_io)
11
+ end
12
+
13
+ def after_feature(feature)
14
+ unless @lines.empty?
15
+ lock_output do
16
+ @lines.each do |line|
17
+ @io.puts "#{feature.file}:#{line}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end