Skip to content

Commit 63e9eba

Browse files
authored
cleanup (#1009)
* cleanup * fix * readme * automate readme
1 parent 1a7b6fe commit 63e9eba

File tree

8 files changed

+77
-38
lines changed

8 files changed

+77
-38
lines changed

CHANGELOG.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@
22

33
## Unreleased
44

5-
### Breaking Changes
6-
7-
### Added
8-
9-
### Fixed
5+
only add here if you are working on a PR
106

11-
## 5.3.0 - 2025-05-24
7+
### Breaking Changes
128

139
### Added
1410

1511
- The `--exec-args` option, which allows users to run shell commands in parallel with test files as arguments
1612

13+
### Fixed
14+
1715
## 5.2.0 - 2025-05-08
1816

1917
- The `specify-groups` option supports reading from STDIN when set to `-`

Rakefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,15 @@ task :bundle_all do
3030
end
3131
end
3232
end
33+
34+
desc "render the README option section"
35+
task :readme do
36+
output = `bundle exec ./bin/parallel_test -h`
37+
abort "Command failed: #{output}" unless $?.success?
38+
output.sub!(/.*Options are:/m, "") || raise
39+
file = "README.md"
40+
separator = "<!-- rake readme -->"
41+
parts = File.read(file).split(separator)
42+
parts[1] = output
43+
File.write file, parts.join(separator)
44+
end

Readme.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ Setup for non-rails
239239
`parallel_cucumber -n 2 -o '-p foo_profile --tags @only_this_tag or @only_that_tag --format summary'`
240240

241241
Options are:
242-
<!-- copy output from bundle exec ./bin/parallel_test -h -->
242+
<!-- rake readme -->
243243
-n PROCESSES How many processes to use, default: available CPUs
244244
-p, --pattern PATTERN run tests matching this regex pattern
245245
--exclude-pattern PATTERN exclude tests matching this regex pattern
@@ -258,7 +258,7 @@ Options are:
258258
--failure-exit-code INT Specify the exit code to use when tests fail
259259
--specify-groups SPECS Use 'specify-groups' if you want to specify multiple specs running in multiple
260260
processes in a specific formation. Commas indicate specs in the same process,
261-
pipes indicate specs in a new process. If SPECS is a `-` the value for this
261+
pipes indicate specs in a new process. If SPECS is a '-' the value for this
262262
option is read from STDIN instead. Cannot use with --single, --isolate, or
263263
--isolate-n. Ex.
264264
$ parallel_tests -n 3 . --specify-groups '1_spec.rb,2_spec.rb|3_spec.rb'
@@ -268,10 +268,10 @@ Options are:
268268
--only-group GROUP_INDEX[,GROUP_INDEX]
269269
Only run the given group numbers.
270270
Changes `--group-by` default to 'filesize'.
271-
-e, --exec COMMAND execute this code parallel and with ENV['TEST_ENV_NUMBER']
272-
--exec-args COMMAND execute this code parallel with test files as arguments, Ex.
271+
-e, --exec COMMAND execute COMMAND in parallel and with ENV['TEST_ENV_NUMBER']
272+
--exec-args COMMAND execute COMMAND in parallel with test files as arguments, for example:
273273
$ parallel_tests --exec-args echo
274-
echo spec/a_spec.rb spec/b_spec.rb
274+
> echo spec/a_spec.rb spec/b_spec.rb
275275
-o, --test-options 'OPTIONS' execute test commands with those options
276276
-t, --type TYPE test(default) / rspec / cucumber / spinach
277277
--suffix PATTERN override built in test file pattern (should match suffix):
@@ -291,14 +291,17 @@ Options are:
291291
--unknown-runtime SECONDS Use given number as unknown runtime (otherwise use average time)
292292
--first-is-1 Use "1" as TEST_ENV_NUMBER to not reuse the default test environment
293293
--fail-fast Stop all groups when one group fails (best used with --test-options '--fail-fast' if supported
294-
--test-file-limit LIMIT Limit to this number of files per test run by batching (for windows set to ~100 to stay below 8192 max command limit, might have bugs from reusing test-env-number and summarizing partial results)
294+
--test-file-limit LIMIT Limit to this number of files per test run by batching
295+
(for windows set to ~100 to stay below 8192 max command limit, might have bugs from reusing test-env-number
296+
and summarizing partial results)
295297
--verbose Print debug output
296298
--verbose-command Combines options --verbose-process-command and --verbose-rerun-command
297299
--verbose-process-command Print the command that will be executed by each process before it begins
298300
--verbose-rerun-command After a process fails, print the command executed by that process
299301
--quiet Print only tests output
300302
-v, --version Show Version
301303
-h, --help Show this.
304+
<!-- rake readme -->
302305

303306
You can run any kind of code in parallel with -e / --exec
304307

lib/parallel_tests/cli.rb

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,9 @@ def any_test_failed?(test_results)
191191
end
192192

193193
def parse_options!(argv)
194-
newline_padding = " " * 37
194+
newline_padding = 37 # poor man's way of getting a decent table like layout for -h output on 120 char width terminal
195195
options = {}
196+
196197
OptionParser.new do |opts|
197198
opts.banner = <<~BANNER
198199
Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
@@ -205,12 +206,14 @@ def parse_options!(argv)
205206
206207
Options are:
207208
BANNER
209+
208210
opts.on("-n PROCESSES", Integer, "How many processes to use, default: available CPUs") { |n| options[:count] = n }
209211
opts.on("-p", "--pattern PATTERN", "run tests matching this regex pattern") { |pattern| options[:pattern] = /#{pattern}/ }
210212
opts.on("--exclude-pattern", "--exclude-pattern PATTERN", "exclude tests matching this regex pattern") { |pattern| options[:exclude_pattern] = /#{pattern}/ }
213+
211214
opts.on(
212215
"--group-by TYPE",
213-
<<~TEXT.rstrip.split("\n").join("\n#{newline_padding}")
216+
heredoc(<<~TEXT, newline_padding)
214217
group tests by:
215218
found - order of finding files
216219
steps - number of cucumber/spinach steps
@@ -220,6 +223,7 @@ def parse_options!(argv)
220223
default - runtime when runtime log is filled otherwise filesize
221224
TEXT
222225
) { |type| options[:group_by] = type.to_sym }
226+
223227
opts.on("-m COUNT", "--multiply-processes COUNT", Float, "use given number as a multiplier of processes to run") do |m|
224228
options[:multiply_processes] = m
225229
end
@@ -251,7 +255,7 @@ def parse_options!(argv)
251255

252256
opts.on(
253257
"--specify-groups SPECS",
254-
<<~TEXT.rstrip.split("\n").join("\n#{newline_padding}")
258+
heredoc(<<~TEXT, newline_padding)
255259
Use 'specify-groups' if you want to specify multiple specs running in multiple
256260
processes in a specific formation. Commas indicate specs in the same process,
257261
pipes indicate specs in a new process. If SPECS is a '-' the value for this
@@ -267,34 +271,40 @@ def parse_options!(argv)
267271
opts.on(
268272
"--only-group GROUP_INDEX[,GROUP_INDEX]",
269273
Array,
270-
<<~TEXT.rstrip.split("\n").join("\n#{newline_padding}")
274+
heredoc(<<~TEXT, newline_padding)
271275
Only run the given group numbers.
272276
Changes `--group-by` default to 'filesize'.
273277
TEXT
274278
) { |groups| options[:only_group] = groups.map(&:to_i) }
275279

276-
opts.on("-e", "--exec COMMAND", "execute this code parallel and with ENV['TEST_ENV_NUMBER']") { |arg| options[:execute] = Shellwords.shellsplit(arg) }
277-
opts.on("--exec-args COMMAND", <<~TEXT.rstrip.split("\n").join("\n#{newline_padding}")
278-
execute this code parallel with test files as arguments, Ex.
279-
$ parallel_tests --exec-args echo
280-
echo spec/a_spec.rb spec/b_spec.rb#{' '}
281-
TEXT
280+
opts.on("-e", "--exec COMMAND", "execute COMMAND in parallel and with ENV['TEST_ENV_NUMBER']") { |arg| options[:execute] = Shellwords.shellsplit(arg) }
281+
opts.on(
282+
"--exec-args COMMAND",
283+
heredoc(<<~TEXT, newline_padding)
284+
execute COMMAND in parallel with test files as arguments, for example:
285+
$ parallel_tests --exec-args echo
286+
> echo spec/a_spec.rb spec/b_spec.rb
287+
TEXT
282288
) { |arg| options[:execute_args] = Shellwords.shellsplit(arg) }
289+
283290
opts.on("-o", "--test-options 'OPTIONS'", "execute test commands with those options") { |arg| options[:test_options] = Shellwords.shellsplit(arg) }
291+
284292
opts.on("-t", "--type TYPE", "test(default) / rspec / cucumber / spinach") do |type|
285293
@runner = load_runner(type)
286294
rescue NameError, LoadError => e
287295
puts "Runner for `#{type}` type has not been found! (#{e})"
288296
abort
289297
end
298+
290299
opts.on(
291300
"--suffix PATTERN",
292-
<<~TEXT.rstrip.split("\n").join("\n#{newline_padding}")
301+
heredoc(<<~TEXT, newline_padding)
293302
override built in test file pattern (should match suffix):
294303
'_spec.rb$' - matches rspec files
295304
'_(test|spec).rb$' - matches test or spec files
296305
TEXT
297306
) { |pattern| options[:suffix] = /#{pattern}/ }
307+
298308
opts.on("--serialize-stdout", "Serialize stdout output, nothing will be written until everything is done") { options[:serialize_stdout] = true }
299309
opts.on("--prefix-output-with-test-env-number", "Prefixes test env number to the output when not using --serialize-stdout") { options[:prefix_output_with_test_env_number] = true }
300310
opts.on("--combine-stderr", "Combine stderr into stdout, useful in conjunction with --serialize-stdout") { options[:combine_stderr] = true }
@@ -308,7 +318,17 @@ def parse_options!(argv)
308318
opts.on("--unknown-runtime SECONDS", Float, "Use given number as unknown runtime (otherwise use average time)") { |time| options[:unknown_runtime] = time }
309319
opts.on("--first-is-1", "Use \"1\" as TEST_ENV_NUMBER to not reuse the default test environment") { options[:first_is_1] = true }
310320
opts.on("--fail-fast", "Stop all groups when one group fails (best used with --test-options '--fail-fast' if supported") { options[:fail_fast] = true }
311-
opts.on("--test-file-limit LIMIT", Integer, "Limit to this number of files per test run by batching (for windows set to ~100 to stay below 8192 max command limit, might have bugs from reusing test-env-number and summarizing partial results)") { |limit| options[:test_file_limit] = limit }
321+
322+
opts.on(
323+
"--test-file-limit LIMIT",
324+
Integer,
325+
heredoc(<<~TEXT, newline_padding)
326+
Limit to this number of files per test run by batching
327+
(for windows set to ~100 to stay below 8192 max command limit, might have bugs from reusing test-env-number
328+
and summarizing partial results)
329+
TEXT
330+
) { |limit| options[:test_file_limit] = limit }
331+
312332
opts.on("--verbose", "Print debug output") { options[:verbose] = true }
313333
opts.on("--verbose-command", "Combines options --verbose-process-command and --verbose-rerun-command") { options.merge! verbose_process_command: true, verbose_rerun_command: true }
314334
opts.on("--verbose-process-command", "Print the command that will be executed by each process before it begins") { options[:verbose_process_command] = true }
@@ -458,5 +478,9 @@ def simulate_output_for_ci(simulate)
458478
yield
459479
end
460480
end
481+
482+
def heredoc(text, newline_padding)
483+
text.rstrip.gsub("\n", "\n#{' ' * newline_padding}")
484+
end
461485
end
462486
end

lib/parallel_tests/gherkin/runner.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def run_tests(test_files, process_number, num_processes, options)
1818
options[:env] ||= {}
1919
options[:env] = options[:env].merge({ 'AUTOTEST' => '1' }) if $stdout.tty?
2020

21-
execute_command(get_cmd(combined_scenarios, options), process_number, num_processes, options)
21+
execute_command(build_command(combined_scenarios, options), process_number, num_processes, options)
2222
end
2323

2424
def test_file_name
@@ -37,8 +37,8 @@ def line_is_result?(line)
3737
line =~ /^\d+ (steps?|scenarios?)/
3838
end
3939

40-
def build_cmd(file_list, options)
41-
cmd = [
40+
def build_test_command(file_list, options)
41+
[
4242
*executable,
4343
*(runtime_logging if File.directory?(File.dirname(runtime_log))),
4444
*file_list,

lib/parallel_tests/rspec/runner.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module RSpec
66
class Runner < ParallelTests::Test::Runner
77
class << self
88
def run_tests(test_files, process_number, num_processes, options)
9-
execute_command(get_cmd(test_files, options), process_number, num_processes, options)
9+
execute_command(build_command(test_files, options), process_number, num_processes, options)
1010
end
1111

1212
def determine_executable
@@ -41,7 +41,7 @@ def line_is_result?(line)
4141
line =~ /\d+ examples?, \d+ failures?/
4242
end
4343

44-
def build_cmd(file_list, options)
44+
def build_test_command(file_list, options)
4545
[*executable, *options[:test_options], *color, *spec_opts, *file_list]
4646
end
4747

lib/parallel_tests/test/runner.rb

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_file_name
2828

2929
def run_tests(test_files, process_number, num_processes, options)
3030
require_list = test_files.map { |file| file.gsub(" ", "\\ ") }.join(" ")
31-
execute_command(get_cmd(require_list, options), process_number, num_processes, options)
31+
execute_command(build_command(require_list, options), process_number, num_processes, options)
3232
end
3333

3434
# ignores other commands runner noise
@@ -160,20 +160,21 @@ def determine_executable
160160
["ruby"]
161161
end
162162

163-
def get_cmd(file_list, options = {})
163+
def build_command(file_list, options)
164164
if options[:execute_args]
165-
[*options[:execute_args], *file_list]
165+
options[:execute_args] + file_list
166166
else
167-
build_cmd(file_list, options)
167+
build_test_command(file_list, options)
168168
end
169169
end
170170

171-
def build_cmd(file_list, options = {})
171+
# load all test files, to be overwritten by other runners
172+
def build_test_command(file_list, options)
172173
[
173174
*executable,
174-
'-Itest',
175+
'-Itest', # adding ./test directory to the load path for compatibility to common setups
175176
'-e',
176-
"%w[#{file_list}].each { |f| require %{./\#{f}} }",
177+
"%w[#{file_list}].each { |f| require %{./\#{f}} }", # using %w to keep things readable
177178
'--',
178179
*options[:test_options]
179180
]

spec/integration_spec.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,11 @@ def test_unicode
346346
end
347347

348348
it "runs two commands in parallel with files as arguments" do
349-
write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
349+
write 'spec/xxx_spec.rb', 'p ARGV; describe("it"){it("should"){puts "TEST1"}}'
350350
write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){puts "TEST2"}}'
351351

352-
result = run_tests "spec", type: 'rspec', add: ["--exec-args", "echo 'hello world' && rspec"]
352+
# need to `--` so sh uses them as arguments that then go into $@
353+
result = run_tests "spec", type: 'rspec', add: ["--exec-args", "sh -c \"echo 'hello world' && rspec $@\" --"]
353354

354355
expect(result).to include_exactly_times('hello world', 2)
355356
expect(result).to include_exactly_times('TEST1', 1)

0 commit comments

Comments
 (0)