Skip to content

Commit 1a7b6fe

Browse files
authored
enhancement: added exec-args option (grosser#1008)
1 parent c017185 commit 1a7b6fe

File tree

8 files changed

+75
-16
lines changed

8 files changed

+75
-16
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99
### Fixed
1010

11+
## 5.3.0 - 2025-05-24
12+
13+
### Added
14+
15+
- The `--exec-args` option, which allows users to run shell commands in parallel with test files as arguments
16+
1117
## 5.2.0 - 2025-05-08
1218

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

Readme.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ Options are:
269269
Only run the given group numbers.
270270
Changes `--group-by` default to 'filesize'.
271271
-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.
273+
$ parallel_tests --exec-args echo
274+
echo spec/a_spec.rb spec/b_spec.rb
272275
-o, --test-options 'OPTIONS' execute test commands with those options
273276
-t, --type TYPE test(default) / rspec / cucumber / spinach
274277
--suffix PATTERN override built in test file pattern (should match suffix):

lib/parallel_tests/cli.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ def parse_options!(argv)
274274
) { |groups| options[:only_group] = groups.map(&:to_i) }
275275

276276
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
282+
) { |arg| options[:execute_args] = Shellwords.shellsplit(arg) }
277283
opts.on("-o", "--test-options 'OPTIONS'", "execute test commands with those options") { |arg| options[:test_options] = Shellwords.shellsplit(arg) }
278284
opts.on("-t", "--type TYPE", "test(default) / rspec / cucumber / spinach") do |type|
279285
@runner = load_runner(type)

lib/parallel_tests/gherkin/runner.rb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +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-
cmd = executable
22-
cmd += runtime_logging if File.directory?(File.dirname(runtime_log))
23-
cmd += combined_scenarios
24-
cmd += cucumber_opts(options[:test_options])
25-
execute_command(cmd, process_number, num_processes, options)
21+
execute_command(get_cmd(combined_scenarios, options), process_number, num_processes, options)
2622
end
2723

2824
def test_file_name
@@ -41,6 +37,15 @@ def line_is_result?(line)
4137
line =~ /^\d+ (steps?|scenarios?)/
4238
end
4339

40+
def build_cmd(file_list, options)
41+
cmd = [
42+
*executable,
43+
*(runtime_logging if File.directory?(File.dirname(runtime_log))),
44+
*file_list,
45+
*cucumber_opts(options[:test_options])
46+
]
47+
end
48+
4449
# cucumber has 2 result lines per test run, that cannot be added
4550
# 1 scenario (1 failed)
4651
# 1 step (1 failed)

lib/parallel_tests/rspec/runner.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +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-
cmd = [*executable, *options[:test_options], *color, *spec_opts, *test_files]
10-
execute_command(cmd, process_number, num_processes, options)
9+
execute_command(get_cmd(test_files, options), process_number, num_processes, options)
1110
end
1211

1312
def determine_executable
@@ -42,6 +41,10 @@ def line_is_result?(line)
4241
line =~ /\d+ examples?, \d+ failures?/
4342
end
4443

44+
def build_cmd(file_list, options)
45+
[*executable, *options[:test_options], *color, *spec_opts, *file_list]
46+
end
47+
4548
# remove old seed and add new seed
4649
# --seed 1234
4750
# --order rand

lib/parallel_tests/test/runner.rb

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +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-
cmd = [
32-
*executable,
33-
'-Itest',
34-
'-e',
35-
"%w[#{require_list}].each { |f| require %{./\#{f}} }",
36-
'--',
37-
*options[:test_options]
38-
]
39-
execute_command(cmd, process_number, num_processes, options)
31+
execute_command(get_cmd(require_list, options), process_number, num_processes, options)
4032
end
4133

4234
# ignores other commands runner noise
@@ -168,6 +160,25 @@ def determine_executable
168160
["ruby"]
169161
end
170162

163+
def get_cmd(file_list, options = {})
164+
if options[:execute_args]
165+
[*options[:execute_args], *file_list]
166+
else
167+
build_cmd(file_list, options)
168+
end
169+
end
170+
171+
def build_cmd(file_list, options = {})
172+
[
173+
*executable,
174+
'-Itest',
175+
'-e',
176+
"%w[#{file_list}].each { |f| require %{./\#{f}} }",
177+
'--',
178+
*options[:test_options]
179+
]
180+
end
181+
171182
def sum_up_results(results)
172183
results = results.join(' ').gsub(/s\b/, '') # combine and singularize results
173184
counts = results.scan(/(\d+) (\w+)/)

spec/integration_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,27 @@ def test_unicode
335335
expect(result).not_to match(/2.*0.*2/m)
336336
end
337337

338+
it "runs command in parallel with files as arguments" do
339+
write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
340+
write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){puts "TEST2"}}'
341+
342+
result = run_tests "spec", type: 'rspec', add: ["--exec-args", "echo"]
343+
344+
expect(result).to include_exactly_times('spec/xxx_spec.rb', 1)
345+
expect(result).to include_exactly_times('spec/xxx2_spec.rb', 1)
346+
end
347+
348+
it "runs two commands in parallel with files as arguments" do
349+
write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
350+
write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){puts "TEST2"}}'
351+
352+
result = run_tests "spec", type: 'rspec', add: ["--exec-args", "echo 'hello world' && rspec"]
353+
354+
expect(result).to include_exactly_times('hello world', 2)
355+
expect(result).to include_exactly_times('TEST1', 1)
356+
expect(result).to include_exactly_times('TEST2', 1)
357+
end
358+
338359
it "exists with success if all sub-processes returned success" do
339360
expect(system(*executable, '-e', 'cat /dev/null', '-n', '4')).to eq(true)
340361
end

spec/parallel_tests/cli_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ def call(*args)
2626
expect(call(["--exec", "echo"])).to eq(execute: ["echo"])
2727
end
2828

29+
it "parses execute arguments" do
30+
expect(call(["test", "--exec-args", "echo"])).to eq(defaults.merge(execute_args: ["echo"]))
31+
end
32+
2933
it "parses excludes pattern" do
3034
expect(call(["test", "--exclude-pattern", "spec/"])).to eq(defaults.merge(exclude_pattern: /spec\//))
3135
end

0 commit comments

Comments
 (0)