parallel_tests 4.2.2 → 4.6.0

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.
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