parallel_tests 4.2.2 → 4.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8669ce686a0c750d7bb0cbcab2f78ddd1d2431bcb5eb3eeea3a1a9050efe345e
4
- data.tar.gz: a7f4d4148379cee9f80a820b72ce04e91eaf8df697cc9db8c2d530225e519f67
3
+ metadata.gz: 82339af23324161c5818c27e55fdadcbcac91ab832f76ce791a6deb289f77233
4
+ data.tar.gz: 0fd923d52f7e368ef4c8e99e7874a465c7213960b1fd6e455404e472dc00ab90
5
5
  SHA512:
6
- metadata.gz: 49a18035b13cb9d5665d73eec21bcc0749ae09b1488d05dc86bfc2516a6303b8e95919ec73ffc648d1aa7f5be373b1c70e8691d69719aaff79c382879db2c6c5
7
- data.tar.gz: 5f0c59c7e8ba42150396e48cf31d437b0b9ce15b20c4554ae23072e00d3b9a8b48de41fd102f17275ca1015e151482b691159d6d10cebe9139106ad8183f2828
6
+ metadata.gz: ee5cb110a617b78e8761c5e01894827b63cccbe6a239b491841949bf2ebe2e9d68a43d4bb0d0a5600cdc6904e8cffba816eb424ca5883865daf661cf7b208f91
7
+ data.tar.gz: e23de90f4aed3c4571252385a251c3765718c3e2fd34aaf342307667b6710819aff652b4c1709dab2152fe0b81217f2d2fb1b98cf308d8c40e3190d91374337d
data/Readme.md CHANGED
@@ -3,8 +3,8 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/parallel_tests.svg)](https://rubygems.org/gems/parallel_tests)
4
4
  [![Build status](https://github.com/grosser/parallel_tests/workflows/test/badge.svg)](https://github.com/grosser/parallel_tests/actions?query=workflow%3Atest)
5
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.
6
+ Speedup Minitest + RSpec + Turnip + Cucumber + Spinach by running parallel on multiple CPU cores.<br/>
7
+ ParallelTests splits tests into balanced groups (by number of lines or runtime) and runs each group in a process with its own database.
8
8
 
9
9
  Setup for Rails
10
10
  ===============
@@ -33,20 +33,30 @@ test:
33
33
  ### Create additional database(s)
34
34
  rake parallel:create
35
35
 
36
+ ### (Multi-DB) Create individual database
37
+ rake parallel:create:<database>
38
+ rake parallel:create:secondary
39
+
36
40
  ### Copy development schema (repeat after migrations)
37
41
  rake parallel:prepare
38
42
 
39
43
  ### Run migrations in additional database(s) (repeat after migrations)
40
44
  rake parallel:migrate
41
45
 
46
+ ### (Multi-DB) Run migrations in individual database
47
+ rake parallel:migrate:<database>
48
+
42
49
  ### Setup environment from scratch (create db and loads schema, useful for CI)
43
50
  rake parallel:setup
44
51
 
45
52
  ### Drop all test databases
46
53
  rake parallel:drop
47
54
 
55
+ ### (Multi-DB) Drop individual test database
56
+ rake parallel:drop:<database>
57
+
48
58
  ### Run!
49
- rake parallel:test # Test::Unit
59
+ rake parallel:test # Minitest
50
60
  rake parallel:spec # RSpec
51
61
  rake parallel:features # Cucumber
52
62
  rake parallel:features-spinach # Spinach
@@ -106,11 +116,13 @@ at_exit do
106
116
  end
107
117
  ```
108
118
 
109
- Even test group run-times
110
- =========================
119
+ Even test group runtimes
120
+ ========================
121
+
122
+ Test groups will often run for different times, making the full test run as slow as the slowest group.
111
123
 
112
- Test groups are often not balanced and will run for different times, making everything wait for the slowest group.
113
- Use these loggers to record test runtime and then use the recorded runtime to balance test groups more evenly.
124
+ Step 1: Use these loggers (see below) to record test runtime
125
+ Step 2: Your next run will use the recorded test runtimes (use `--runtime-log <file>` if you picked a location different from below)
114
126
 
115
127
  ### RSpec
116
128
 
@@ -128,9 +140,11 @@ Add to your `test_helper.rb`:
128
140
  require 'parallel_tests/test/runtime_logger' if ENV['RECORD_RUNTIME']
129
141
  ```
130
142
 
131
- results will be logged to tmp/parallel_runtime_test.log when `RECORD_RUNTIME` is set,
143
+ results will be logged to `tmp/parallel_runtime_test.log` when `RECORD_RUNTIME` is set,
132
144
  so it is not always required or overwritten.
133
145
 
146
+ ### TODO: add instructions for other frameworks
147
+
134
148
  Loggers
135
149
  =======
136
150
 
@@ -147,7 +161,7 @@ Add the following to your `.rspec_parallel` (or `.rspec`) :
147
161
  RSpec: FailuresLogger
148
162
  -----------------------
149
163
 
150
- Produce pastable command-line snippets for each failed example. For example:
164
+ Produce pasteable command-line snippets for each failed example. For example:
151
165
 
152
166
  ```bash
153
167
  rspec /path/to/my_spec.rb:123 # should do something
@@ -160,6 +174,24 @@ Add to `.rspec_parallel` or use as CLI flag:
160
174
 
161
175
  (Not needed to retry failures, for that pass [--only-failures](https://relishapp.com/rspec/rspec-core/docs/command-line/only-failures) to rspec)
162
176
 
177
+
178
+ RSpec: VerboseLogger
179
+ -----------------------
180
+
181
+ Prints a single line for starting and finishing each example, to see what is currently running in each process.
182
+
183
+ ```
184
+ # PID, parallel process number, spec status, example description
185
+ [14403] [2] [STARTED] Foo foo
186
+ [14402] [1] [STARTED] Bar bar
187
+ [14402] [1] [PASSED] Bar bar
188
+ ```
189
+
190
+ Add to `.rspec_parallel` or use as CLI flag:
191
+
192
+ --format ParallelTests::RSpec::VerboseLogger
193
+
194
+
163
195
  Cucumber: FailuresLogger
164
196
  -----------------------
165
197
 
@@ -247,6 +279,7 @@ Options are:
247
279
  --nice execute test commands with low priority.
248
280
  --runtime-log [PATH] Location of previously recorded test runtimes
249
281
  --allowed-missing [INT] Allowed percentage of missing runtimes (default = 50)
282
+ --allow-duplicates When detecting files to run, allow duplicates. Useful for local debugging
250
283
  --unknown-runtime [FLOAT] Use given number as unknown runtime (otherwise use average time)
251
284
  --first-is-1 Use "1" as TEST_ENV_NUMBER to not reuse the default test environment
252
285
  --fail-fast Stop all groups when one group fails (best used with --test-options '--fail-fast' if supported
@@ -405,6 +438,8 @@ inspired by [pivotal labs](https://blog.pivotal.io/labs/labs/parallelize-your-rs
405
438
  - [Jon Dufresne](https://github.com/jdufresne)
406
439
  - [Eric Kessler](https://github.com/enkessler)
407
440
  - [Adis Osmonov](https://github.com/adis-io)
441
+ - [Josh Westbrook](https://github.com/joshwestbrook)
442
+ - [Jay Dorsey](https://github.com/jaydorsey)
408
443
 
409
444
  [Michael Grosser](http://grosser.it)<br/>
410
445
  michael@grosser.it<br/>
@@ -278,6 +278,7 @@ module ParallelTests
278
278
  opts.on("--nice", "execute test commands with low priority.") { options[:nice] = true }
279
279
  opts.on("--runtime-log [PATH]", "Location of previously recorded test runtimes") { |path| options[:runtime_log] = path }
280
280
  opts.on("--allowed-missing [INT]", Integer, "Allowed percentage of missing runtimes (default = 50)") { |percent| options[:allowed_missing_percent] = percent }
281
+ opts.on('--allow-duplicates', 'When detecting files to run, allow duplicates') { options[:allow_duplicates] = true }
281
282
  opts.on("--unknown-runtime [FLOAT]", Float, "Use given number as unknown runtime (otherwise use average time)") { |time| options[:unknown_runtime] = time }
282
283
  opts.on("--first-is-1", "Use \"1\" as TEST_ENV_NUMBER to not reuse the default test environment") { options[:first_is_1] = true }
283
284
  opts.on("--fail-fast", "Stop all groups when one group fails (best used with --test-options '--fail-fast' if supported") { options[:fail_fast] = true }
@@ -33,8 +33,10 @@ module ParallelTests
33
33
  "spec"
34
34
  end
35
35
 
36
+ # used to find all _spec.rb files
37
+ # supports also feature files used by rspec turnip extension
36
38
  def test_suffix
37
- /_spec\.rb$/
39
+ /(_spec\.rb|\.feature)$/
38
40
  end
39
41
 
40
42
  def line_is_result?(line)
@@ -36,6 +36,8 @@ class ParallelTests::RSpec::RuntimeLogger < ParallelTests::RSpec::LoggerBase
36
36
  def start_dump(*)
37
37
  return unless ENV['TEST_ENV_NUMBER'] # only record when running in parallel
38
38
  lock_output do
39
+ # Order the output from slowest to fastest
40
+ @example_times = @example_times.sort_by(&:last).reverse
39
41
  @example_times.each do |file, time|
40
42
  relative_path = file.sub(%r{^#{Regexp.escape Dir.pwd}/}, '').sub(%r{^\./}, "")
41
43
  @output.puts "#{relative_path}:#{[time, 0].max}"
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core/formatters/base_text_formatter'
4
+ require 'parallel_tests/rspec/runner'
5
+
6
+ class ParallelTests::RSpec::VerboseLogger < RSpec::Core::Formatters::BaseTextFormatter
7
+ RSpec::Core::Formatters.register(
8
+ self,
9
+ :example_group_started,
10
+ :example_group_finished,
11
+ :example_started,
12
+ :example_passed,
13
+ :example_pending,
14
+ :example_failed
15
+ )
16
+
17
+ def initialize(output)
18
+ super
19
+ @line = []
20
+ end
21
+
22
+ def example_group_started(notification)
23
+ @line.push(notification.group.description)
24
+ end
25
+
26
+ def example_group_finished(_notification)
27
+ @line.pop
28
+ end
29
+
30
+ def example_started(notification)
31
+ @line.push(notification.example.description)
32
+ output_formatted_line('STARTED', :yellow)
33
+ end
34
+
35
+ def example_passed(_passed)
36
+ output_formatted_line('PASSED', :success)
37
+ @line.pop
38
+ end
39
+
40
+ def example_pending(_pending)
41
+ output_formatted_line('PENDING', :pending)
42
+ @line.pop
43
+ end
44
+
45
+ def example_failed(_failure)
46
+ output_formatted_line('FAILED', :failure)
47
+ @line.pop
48
+ end
49
+
50
+ private
51
+
52
+ def output_formatted_line(status, console_code)
53
+ prefix = ["[#{Process.pid}]"]
54
+ if ENV.include?('TEST_ENV_NUMBER')
55
+ test_env_number = ENV['TEST_ENV_NUMBER'] == '' ? 1 : Integer(ENV['TEST_ENV_NUMBER'])
56
+ prefix << "[#{test_env_number}]"
57
+ end
58
+ prefix << RSpec::Core::Formatters::ConsoleCodes.wrap("[#{status}]", console_code)
59
+
60
+ output.puts [*prefix, *@line].join(' ')
61
+ end
62
+ end
@@ -15,7 +15,7 @@ module ParallelTests
15
15
 
16
16
  def runtime_logging
17
17
  # Not Yet Supported
18
- ""
18
+ []
19
19
  end
20
20
  end
21
21
  end
@@ -48,6 +48,9 @@ module ParallelTests
48
48
  activate_pipefail = "set -o pipefail"
49
49
  remove_ignored_lines = %{(grep -v #{Shellwords.escape(ignore_regex)} || true)}
50
50
 
51
+ # remove nil values (ex: #purge_before_load returns nil)
52
+ command.compact!
53
+
51
54
  if system('/bin/bash', '-c', "#{activate_pipefail} 2>/dev/null")
52
55
  shell_command = "#{activate_pipefail} && (#{Shellwords.shelljoin(command)}) | #{remove_ignored_lines}"
53
56
  ['/bin/bash', '-c', shell_command]
@@ -125,6 +128,24 @@ module ParallelTests
125
128
  command
126
129
  end
127
130
 
131
+ def configured_databases
132
+ return [] unless defined?(ActiveRecord) && rails_61_or_greater?
133
+
134
+ @@configured_databases ||= ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
135
+ end
136
+
137
+ def for_each_database(&block)
138
+ # Use nil to represent all databases
139
+ block&.call(nil)
140
+
141
+ # skip if not rails or old rails version
142
+ return if !defined?(ActiveRecord::Tasks::DatabaseTasks) || !ActiveRecord::Tasks::DatabaseTasks.respond_to?(:for_each)
143
+
144
+ ActiveRecord::Tasks::DatabaseTasks.for_each(configured_databases) do |name|
145
+ block&.call(name)
146
+ end
147
+ end
148
+
128
149
  private
129
150
 
130
151
  def rails_7_or_greater?
@@ -145,25 +166,33 @@ namespace :parallel do
145
166
  ParallelTests::Tasks.run_in_parallel(ParallelTests::Tasks.suppress_schema_load_output(command), args)
146
167
  end
147
168
 
148
- desc "Create test databases via db:create --> parallel:create[num_cpus]"
149
- task :create, :count do |_, args|
150
- ParallelTests::Tasks.run_in_parallel(
151
- [$0, "db:create", "RAILS_ENV=#{ParallelTests::Tasks.rails_env}"],
152
- args
153
- )
169
+ ParallelTests::Tasks.for_each_database do |name|
170
+ task_name = 'create'
171
+ task_name += ":#{name}" if name
172
+ desc "Create test#{" #{name}" if name} database via db:#{task_name} --> parallel:#{task_name}[num_cpus]"
173
+ task task_name.to_sym, :count do |_, args|
174
+ ParallelTests::Tasks.run_in_parallel(
175
+ [$0, "db:#{task_name}", "RAILS_ENV=#{ParallelTests::Tasks.rails_env}"],
176
+ args
177
+ )
178
+ end
154
179
  end
155
180
 
156
- desc "Drop test databases via db:drop --> parallel:drop[num_cpus]"
157
- task :drop, :count do |_, args|
158
- ParallelTests::Tasks.run_in_parallel(
159
- [
160
- $0,
161
- "db:drop",
162
- "RAILS_ENV=#{ParallelTests::Tasks.rails_env}",
163
- "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
164
- ],
165
- args
166
- )
181
+ ParallelTests::Tasks.for_each_database do |name|
182
+ task_name = 'drop'
183
+ task_name += ":#{name}" if name
184
+ desc "Drop test#{" #{name}" if name} database via db:#{task_name} --> parallel:#{task_name}[num_cpus]"
185
+ task task_name.to_sym, :count do |_, args|
186
+ ParallelTests::Tasks.run_in_parallel(
187
+ [
188
+ $0,
189
+ "db:#{task_name}",
190
+ "RAILS_ENV=#{ParallelTests::Tasks.rails_env}",
191
+ "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
192
+ ],
193
+ args
194
+ )
195
+ end
167
196
  end
168
197
 
169
198
  desc "Update test databases by dumping and loading --> parallel:prepare[num_cpus]"
@@ -190,12 +219,16 @@ namespace :parallel do
190
219
  end
191
220
 
192
221
  # when dumping/resetting takes too long
193
- desc "Update test databases via db:migrate --> parallel:migrate[num_cpus]"
194
- task :migrate, :count do |_, args|
195
- ParallelTests::Tasks.run_in_parallel(
196
- [$0, "db:migrate", "RAILS_ENV=#{ParallelTests::Tasks.rails_env}"],
197
- args
198
- )
222
+ ParallelTests::Tasks.for_each_database do |name|
223
+ task_name = 'migrate'
224
+ task_name += ":#{name}" if name
225
+ desc "Update test#{" #{name}" if name} database via db:#{task_name} --> parallel:#{task_name}[num_cpus]"
226
+ task task_name.to_sym, :count do |_, args|
227
+ ParallelTests::Tasks.run_in_parallel(
228
+ [$0, "db:#{task_name}", "RAILS_ENV=#{ParallelTests::Tasks.rails_env}"],
229
+ args
230
+ )
231
+ end
199
232
  end
200
233
 
201
234
  desc "Rollback test databases via db:rollback --> parallel:rollback[num_cpus]"
@@ -207,16 +240,24 @@ namespace :parallel do
207
240
  end
208
241
 
209
242
  # just load the schema (good for integration server <-> no development db)
210
- desc "Load dumped schema for test databases via db:schema:load --> parallel:load_schema[num_cpus]"
211
- task :load_schema, :count do |_, args|
212
- command = [
213
- $0,
214
- ParallelTests::Tasks.purge_before_load,
215
- "db:schema:load",
216
- "RAILS_ENV=#{ParallelTests::Tasks.rails_env}",
217
- "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
218
- ]
219
- ParallelTests::Tasks.run_in_parallel(ParallelTests::Tasks.suppress_schema_load_output(command), args)
243
+ ParallelTests::Tasks.for_each_database do |name|
244
+ rails_task = 'db:schema:load'
245
+ rails_task += ":#{name}" if name
246
+
247
+ task_name = 'load_schema'
248
+ task_name += ":#{name}" if name
249
+
250
+ desc "Load dumped schema for test#{" #{name}" if name} database via #{rails_task} --> parallel:#{task_name}[num_cpus]"
251
+ task task_name.to_sym, :count do |_, args|
252
+ command = [
253
+ $0,
254
+ ParallelTests::Tasks.purge_before_load,
255
+ rails_task,
256
+ "RAILS_ENV=#{ParallelTests::Tasks.rails_env}",
257
+ "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
258
+ ]
259
+ ParallelTests::Tasks.run_in_parallel(ParallelTests::Tasks.suppress_schema_load_output(command), args)
260
+ end
220
261
  end
221
262
 
222
263
  # load the structure from the structure.sql file
@@ -238,8 +238,9 @@ module ParallelTests
238
238
  suffix_pattern = options[:suffix] || test_suffix
239
239
  include_pattern = options[:pattern] || //
240
240
  exclude_pattern = options[:exclude_pattern]
241
+ allow_duplicates = options[:allow_duplicates]
241
242
 
242
- (tests || []).flat_map do |file_or_folder|
243
+ files = (tests || []).flat_map do |file_or_folder|
243
244
  if File.directory?(file_or_folder)
244
245
  files = files_in_folder(file_or_folder, options)
245
246
  files = files.grep(suffix_pattern).grep(include_pattern)
@@ -248,7 +249,9 @@ module ParallelTests
248
249
  else
249
250
  file_or_folder
250
251
  end
251
- end.uniq
252
+ end
253
+
254
+ allow_duplicates ? files : files.uniq
252
255
  end
253
256
 
254
257
  def files_in_folder(folder, options = {})
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ParallelTests
3
- VERSION = '4.2.2'
3
+ VERSION = '4.6.0'
4
4
  end
@@ -44,6 +44,8 @@ module ParallelTests
44
44
 
45
45
  def stop_all_processes
46
46
  pids.all.each { |pid| Process.kill(:INT, pid) }
47
+ rescue Errno::ESRCH
48
+ # Process already terminated, do nothing
47
49
  end
48
50
 
49
51
  # copied from http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.2
4
+ version: 4.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-06 00:00:00.000000000 Z
11
+ date: 2024-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parallel
@@ -58,6 +58,7 @@ files:
58
58
  - lib/parallel_tests/rspec/runner.rb
59
59
  - lib/parallel_tests/rspec/runtime_logger.rb
60
60
  - lib/parallel_tests/rspec/summary_logger.rb
61
+ - lib/parallel_tests/rspec/verbose_logger.rb
61
62
  - lib/parallel_tests/spinach/runner.rb
62
63
  - lib/parallel_tests/tasks.rb
63
64
  - lib/parallel_tests/test/runner.rb
@@ -68,8 +69,8 @@ licenses:
68
69
  - MIT
69
70
  metadata:
70
71
  bug_tracker_uri: https://github.com/grosser/parallel_tests/issues
71
- documentation_uri: https://github.com/grosser/parallel_tests/blob/v4.2.2/Readme.md
72
- source_code_uri: https://github.com/grosser/parallel_tests/tree/v4.2.2
72
+ documentation_uri: https://github.com/grosser/parallel_tests/blob/v4.6.0/Readme.md
73
+ source_code_uri: https://github.com/grosser/parallel_tests/tree/v4.6.0
73
74
  wiki_uri: https://github.com/grosser/parallel_tests/wiki
74
75
  post_install_message:
75
76
  rdoc_options: []
@@ -86,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
87
  - !ruby/object:Gem::Version
87
88
  version: '0'
88
89
  requirements: []
89
- rubygems_version: 3.3.3
90
+ rubygems_version: 3.4.10
90
91
  signing_key:
91
92
  specification_version: 4
92
93
  summary: Run Test::Unit / RSpec / Cucumber / Spinach in parallel