parallel_tests 1.3.7 → 3.7.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.
- checksums.yaml +5 -5
- data/Readme.md +153 -61
- data/bin/parallel_cucumber +2 -1
- data/bin/parallel_rspec +2 -1
- data/bin/parallel_spinach +2 -1
- data/bin/parallel_test +2 -1
- data/lib/parallel_tests/cli.rb +264 -69
- data/lib/parallel_tests/cucumber/failures_logger.rb +10 -8
- data/lib/parallel_tests/cucumber/features_with_steps.rb +32 -0
- data/lib/parallel_tests/cucumber/runner.rb +16 -9
- data/lib/parallel_tests/cucumber/scenario_line_logger.rb +30 -31
- data/lib/parallel_tests/cucumber/scenarios.rb +44 -11
- data/lib/parallel_tests/gherkin/io.rb +2 -3
- data/lib/parallel_tests/gherkin/listener.rb +10 -12
- data/lib/parallel_tests/gherkin/runner.rb +24 -25
- data/lib/parallel_tests/gherkin/runtime_logger.rb +13 -12
- data/lib/parallel_tests/grouper.rb +94 -22
- data/lib/parallel_tests/pids.rb +60 -0
- data/lib/parallel_tests/railtie.rb +1 -0
- data/lib/parallel_tests/rspec/failures_logger.rb +7 -35
- data/lib/parallel_tests/rspec/logger_base.rb +13 -20
- data/lib/parallel_tests/rspec/runner.rb +45 -27
- data/lib/parallel_tests/rspec/runtime_logger.rb +23 -35
- data/lib/parallel_tests/rspec/summary_logger.rb +5 -13
- data/lib/parallel_tests/spinach/runner.rb +6 -2
- data/lib/parallel_tests/tasks.rb +125 -59
- data/lib/parallel_tests/test/runner.rb +102 -62
- data/lib/parallel_tests/test/runtime_logger.rb +33 -61
- data/lib/parallel_tests/version.rb +2 -1
- data/lib/parallel_tests.rb +46 -19
- metadata +12 -7
@@ -1,54 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'parallel_tests/rspec/logger_base'
|
2
3
|
require 'parallel_tests/rspec/runner'
|
3
4
|
|
4
5
|
class ParallelTests::RSpec::FailuresLogger < ParallelTests::RSpec::LoggerBase
|
5
|
-
if
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
# RSpec 1: does not keep track of failures, so we do
|
10
|
-
def example_failed(example, *args)
|
11
|
-
if RSPEC_1
|
12
|
-
@failed_examples ||= []
|
13
|
-
@failed_examples << example
|
14
|
-
else
|
15
|
-
super
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
if RSPEC_1
|
20
|
-
def dump_failure(*args)
|
21
|
-
end
|
6
|
+
if RSPEC_2
|
7
|
+
def dump_failures(*args); end
|
22
8
|
else
|
23
|
-
|
24
|
-
end
|
9
|
+
RSpec::Core::Formatters.register self, :dump_summary
|
25
10
|
end
|
26
11
|
|
27
12
|
def dump_summary(*args)
|
28
13
|
lock_output do
|
29
|
-
if
|
30
|
-
|
31
|
-
|
14
|
+
if RSPEC_2
|
15
|
+
dump_commands_to_rerun_failed_examples
|
16
|
+
else
|
32
17
|
notification = args.first
|
33
18
|
unless notification.failed_examples.empty?
|
34
19
|
colorizer = ::RSpec::Core::Formatters::ConsoleCodes
|
35
20
|
output.puts notification.colorized_rerun_commands(colorizer)
|
36
21
|
end
|
37
|
-
else
|
38
|
-
dump_commands_to_rerun_failed_examples
|
39
22
|
end
|
40
23
|
end
|
41
24
|
@output.flush
|
42
25
|
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def dump_commands_to_rerun_failed_examples_rspec_1
|
47
|
-
(@failed_examples||[]).each do |example|
|
48
|
-
file, line = example.location.to_s.split(':')
|
49
|
-
next unless file and line
|
50
|
-
file.gsub!(%r(^.*?/spec/), './spec/')
|
51
|
-
@output.puts "#{ParallelTests::RSpec::Runner.send(:executable)} #{file}:#{line} # #{example.description}"
|
52
|
-
end
|
53
|
-
end
|
54
26
|
end
|
@@ -1,47 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module ParallelTests
|
2
3
|
module RSpec
|
3
4
|
end
|
4
5
|
end
|
5
6
|
|
6
|
-
|
7
|
-
require 'rspec/core/formatters/base_text_formatter'
|
8
|
-
base = RSpec::Core::Formatters::BaseTextFormatter
|
9
|
-
rescue LoadError
|
10
|
-
require 'spec/runner/formatter/base_text_formatter'
|
11
|
-
base = Spec::Runner::Formatter::BaseTextFormatter
|
12
|
-
end
|
13
|
-
|
14
|
-
ParallelTests::RSpec::LoggerBaseBase = base
|
7
|
+
require 'rspec/core/formatters/base_text_formatter'
|
15
8
|
|
16
|
-
class ParallelTests::RSpec::LoggerBase <
|
17
|
-
|
18
|
-
RSPEC_3 = !RSPEC_1 && RSpec::Core::Version::STRING.start_with?('3')
|
9
|
+
class ParallelTests::RSpec::LoggerBase < RSpec::Core::Formatters::BaseTextFormatter
|
10
|
+
RSPEC_2 = RSpec::Core::Version::STRING.start_with?('2')
|
19
11
|
|
20
12
|
def initialize(*args)
|
21
13
|
super
|
22
14
|
|
23
|
-
@output ||= args[
|
15
|
+
@output ||= args[0]
|
24
16
|
|
25
|
-
|
17
|
+
case @output
|
18
|
+
when String # a path ?
|
26
19
|
FileUtils.mkdir_p(File.dirname(@output))
|
27
|
-
File.open(@output, 'w'){} # overwrite previous results
|
20
|
+
File.open(@output, 'w') {} # overwrite previous results
|
28
21
|
@output = File.open(@output, 'a')
|
29
|
-
|
22
|
+
when File # close and restart in append mode
|
30
23
|
@output.close
|
31
24
|
@output = File.open(@output.path, 'a')
|
32
25
|
end
|
33
26
|
end
|
34
27
|
|
35
|
-
#stolen from Rspec
|
36
|
-
def close(*
|
37
|
-
@output.close
|
28
|
+
# stolen from Rspec
|
29
|
+
def close(*)
|
30
|
+
@output.close if (IO === @output) & (@output != $stdout)
|
38
31
|
end
|
39
32
|
|
40
33
|
protected
|
41
34
|
|
42
35
|
# do not let multiple processes get in each others way
|
43
36
|
def lock_output
|
44
|
-
if
|
37
|
+
if @output.is_a?(File)
|
45
38
|
begin
|
46
39
|
@output.flock File::LOCK_EX
|
47
40
|
yield
|
@@ -1,38 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "parallel_tests/test/runner"
|
2
3
|
|
3
4
|
module ParallelTests
|
4
5
|
module RSpec
|
5
6
|
class Runner < ParallelTests::Test::Runner
|
6
7
|
DEV_NULL = (WINDOWS ? "NUL" : "/dev/null")
|
7
|
-
NAME = 'RSpec'
|
8
|
-
|
9
8
|
class << self
|
10
9
|
def run_tests(test_files, process_number, num_processes, options)
|
11
10
|
exe = executable # expensive, so we cache
|
12
|
-
|
13
|
-
cmd = [exe, options[:test_options], (rspec_2_color if version == 2), spec_opts, *test_files].compact.join(" ")
|
14
|
-
options = options.merge(:env => rspec_1_color) if version == 1
|
11
|
+
cmd = [exe, options[:test_options], color, spec_opts, *test_files].compact.join(" ")
|
15
12
|
execute_command(cmd, process_number, num_processes, options)
|
16
13
|
end
|
17
14
|
|
18
15
|
def determine_executable
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
"script/spec"
|
24
|
-
when ParallelTests.bundler_enabled?
|
25
|
-
cmd = (run("bundle show rspec-core") =~ %r{Could not find gem.*} ? "spec" : "rspec")
|
26
|
-
"bundle exec #{cmd}"
|
16
|
+
if File.exist?("bin/rspec")
|
17
|
+
ParallelTests.with_ruby_binary("bin/rspec")
|
18
|
+
elsif ParallelTests.bundler_enabled?
|
19
|
+
"bundle exec rspec"
|
27
20
|
else
|
28
|
-
|
21
|
+
"rspec"
|
29
22
|
end
|
30
|
-
|
31
|
-
cmd or raise("Can't find executables rspec or spec")
|
32
23
|
end
|
33
24
|
|
34
25
|
def runtime_log
|
35
|
-
|
26
|
+
"tmp/parallel_runtime_rspec.log"
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_test_folder
|
30
|
+
"spec"
|
36
31
|
end
|
37
32
|
|
38
33
|
def test_file_name
|
@@ -43,6 +38,37 @@ module ParallelTests
|
|
43
38
|
/_spec\.rb$/
|
44
39
|
end
|
45
40
|
|
41
|
+
def line_is_result?(line)
|
42
|
+
line =~ /\d+ examples?, \d+ failures?/
|
43
|
+
end
|
44
|
+
|
45
|
+
# remove old seed and add new seed
|
46
|
+
# --seed 1234
|
47
|
+
# --order rand
|
48
|
+
# --order rand:1234
|
49
|
+
# --order random:1234
|
50
|
+
def command_with_seed(cmd, seed)
|
51
|
+
clean = cmd.sub(/\s--(seed\s+\d+|order\s+rand(om)?(:\d+)?)\b/, '')
|
52
|
+
"#{clean} --seed #{seed}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Summarize results from threads and colorize results based on failure and pending counts.
|
56
|
+
#
|
57
|
+
def summarize_results(results)
|
58
|
+
text = super
|
59
|
+
return text unless $stdout.tty?
|
60
|
+
sums = sum_up_results(results)
|
61
|
+
color =
|
62
|
+
if sums['failure'] > 0
|
63
|
+
31 # red
|
64
|
+
elsif sums['pending'] > 0
|
65
|
+
33 # yellow
|
66
|
+
else
|
67
|
+
32 # green
|
68
|
+
end
|
69
|
+
"\e[#{color}m#{text}\e[0m"
|
70
|
+
end
|
71
|
+
|
46
72
|
private
|
47
73
|
|
48
74
|
# so it can be stubbed....
|
@@ -50,20 +76,12 @@ module ParallelTests
|
|
50
76
|
`#{cmd}`
|
51
77
|
end
|
52
78
|
|
53
|
-
def
|
54
|
-
if $stdout.tty?
|
55
|
-
{'RSPEC_COLOR' => "1"}
|
56
|
-
else
|
57
|
-
{}
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def rspec_2_color
|
79
|
+
def color
|
62
80
|
'--color --tty' if $stdout.tty?
|
63
81
|
end
|
64
82
|
|
65
83
|
def spec_opts
|
66
|
-
options_file = ['.rspec_parallel', 'spec/parallel_spec.opts', 'spec/spec.opts'].detect{|f| File.file?(f) }
|
84
|
+
options_file = ['.rspec_parallel', 'spec/parallel_spec.opts', 'spec/spec.opts'].detect { |f| File.file?(f) }
|
67
85
|
return unless options_file
|
68
86
|
"-O #{options_file}"
|
69
87
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'parallel_tests'
|
2
3
|
require 'parallel_tests/rspec/logger_base'
|
3
4
|
|
@@ -5,52 +6,39 @@ class ParallelTests::RSpec::RuntimeLogger < ParallelTests::RSpec::LoggerBase
|
|
5
6
|
def initialize(*args)
|
6
7
|
super
|
7
8
|
@example_times = Hash.new(0)
|
8
|
-
@group_nesting = 0
|
9
|
+
@group_nesting = 0
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
RSpec::Core::Formatters.register self, :example_group_started, :example_group_finished, :start_dump unless RSPEC_2
|
13
|
+
|
14
|
+
def example_group_started(example_group)
|
15
|
+
@time = ParallelTests.now if @group_nesting == 0
|
16
|
+
@group_nesting += 1
|
17
|
+
super
|
13
18
|
end
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
20
|
+
def example_group_finished(notification)
|
21
|
+
@group_nesting -= 1
|
22
|
+
if @group_nesting == 0
|
23
|
+
path = (RSPEC_2 ? notification.file_path : notification.group.file_path)
|
24
|
+
@example_times[path] += ParallelTests.now - @time
|
19
25
|
end
|
26
|
+
super if defined?(super)
|
27
|
+
end
|
20
28
|
|
21
|
-
|
22
|
-
file = example.location.split(':').first
|
23
|
-
@example_times[file] += ParallelTests.now - @time
|
24
|
-
super
|
25
|
-
end
|
26
|
-
else
|
27
|
-
def example_group_started(example_group)
|
28
|
-
@time = ParallelTests.now if @group_nesting == 0
|
29
|
-
@group_nesting += 1
|
30
|
-
super
|
31
|
-
end
|
29
|
+
def dump_summary(*); end
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
path = (RSPEC_3 ? notification.group.file_path : notification.file_path)
|
37
|
-
@example_times[path] += ParallelTests.now - @time
|
38
|
-
end
|
39
|
-
super if defined?(super)
|
40
|
-
end
|
41
|
-
end
|
31
|
+
def dump_failures(*); end
|
32
|
+
|
33
|
+
def dump_failure(*); end
|
42
34
|
|
43
|
-
def
|
44
|
-
def dump_failures(*args);end
|
45
|
-
def dump_failure(*args);end
|
46
|
-
def dump_pending(*args);end
|
35
|
+
def dump_pending(*); end
|
47
36
|
|
48
|
-
def start_dump(*
|
49
|
-
return unless ENV['TEST_ENV_NUMBER'] #only record when running in parallel
|
50
|
-
# TODO: Figure out why sometimes time can be less than 0
|
37
|
+
def start_dump(*)
|
38
|
+
return unless ENV['TEST_ENV_NUMBER'] # only record when running in parallel
|
51
39
|
lock_output do
|
52
40
|
@example_times.each do |file, time|
|
53
|
-
relative_path = file.sub(
|
41
|
+
relative_path = file.sub(%r{^#{Regexp.escape Dir.pwd}/}, '').sub(%r{^\./}, "")
|
54
42
|
@output.puts "#{relative_path}:#{time > 0 ? time : 0}"
|
55
43
|
end
|
56
44
|
end
|
@@ -1,19 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'parallel_tests/rspec/failures_logger'
|
2
3
|
|
3
4
|
class ParallelTests::RSpec::SummaryLogger < ParallelTests::RSpec::LoggerBase
|
4
|
-
|
5
|
-
RSpec::Core::Formatters.register self, :dump_failures
|
6
|
-
end
|
5
|
+
RSpec::Core::Formatters.register self, :dump_failures unless RSPEC_2
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@output.flush
|
12
|
-
end
|
13
|
-
else
|
14
|
-
def dump_failures(*args)
|
15
|
-
lock_output { super }
|
16
|
-
@output.flush
|
17
|
-
end
|
7
|
+
def dump_failures(*args)
|
8
|
+
lock_output { super }
|
9
|
+
@output.flush
|
18
10
|
end
|
19
11
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "parallel_tests/gherkin/runner"
|
2
3
|
|
3
4
|
module ParallelTests
|
@@ -8,11 +9,14 @@ module ParallelTests
|
|
8
9
|
'spinach'
|
9
10
|
end
|
10
11
|
|
12
|
+
def default_test_folder
|
13
|
+
'features'
|
14
|
+
end
|
15
|
+
|
11
16
|
def runtime_logging
|
12
|
-
#Not Yet Supported
|
17
|
+
# Not Yet Supported
|
13
18
|
""
|
14
19
|
end
|
15
|
-
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
data/lib/parallel_tests/tasks.rb
CHANGED
@@ -1,21 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'rake'
|
3
|
+
require 'shellwords'
|
2
4
|
|
3
5
|
module ParallelTests
|
4
6
|
module Tasks
|
5
7
|
class << self
|
6
8
|
def rails_env
|
7
|
-
|
9
|
+
'test'
|
8
10
|
end
|
9
11
|
|
10
|
-
def
|
12
|
+
def rake_bin
|
13
|
+
# Prevent 'Exec format error' Errno::ENOEXEC on Windows
|
14
|
+
return "rake" if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
|
15
|
+
binstub_path = File.join('bin', 'rake')
|
16
|
+
return binstub_path if File.exist?(binstub_path)
|
17
|
+
"rake"
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_lib
|
21
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
22
|
+
require "parallel_tests"
|
23
|
+
end
|
24
|
+
|
25
|
+
def purge_before_load
|
26
|
+
if Gem::Version.new(Rails.version) > Gem::Version.new('4.2.0')
|
27
|
+
Rake::Task.task_defined?('db:purge') ? 'db:purge' : 'app:db:purge'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_in_parallel(cmd, options = {})
|
32
|
+
load_lib
|
11
33
|
count = " -n #{options[:count]}" unless options[:count].to_s.empty?
|
12
|
-
|
13
|
-
|
34
|
+
# Using the relative path to find the binary allow to run a specific version of it
|
35
|
+
executable = File.expand_path('../../bin/parallel_test', __dir__)
|
36
|
+
non_parallel = (options[:non_parallel] ? ' --non-parallel' : '')
|
37
|
+
command = "#{ParallelTests.with_ruby_binary(Shellwords.escape(executable))} --exec '#{cmd}'#{count}#{non_parallel}"
|
14
38
|
abort unless system(command)
|
15
39
|
end
|
16
40
|
|
17
41
|
# this is a crazy-complex solution for a very simple problem:
|
18
|
-
# removing certain lines from the output without
|
42
|
+
# removing certain lines from the output without changing the exit-status
|
19
43
|
# normally I'd not do this, but it has been lots of fun and a great learning experience :)
|
20
44
|
#
|
21
45
|
# - sed does not support | without -r
|
@@ -28,17 +52,21 @@ module ParallelTests
|
|
28
52
|
# - simple system "set -o pipefail" returns nil even though set -o pipefail exists with 0
|
29
53
|
def suppress_output(command, ignore_regex)
|
30
54
|
activate_pipefail = "set -o pipefail"
|
31
|
-
remove_ignored_lines = %
|
55
|
+
remove_ignored_lines = %{(grep -v "#{ignore_regex}" || test 1)}
|
32
56
|
|
33
57
|
if File.executable?('/bin/bash') && system('/bin/bash', '-c', "#{activate_pipefail} 2>/dev/null && test 1")
|
34
58
|
# We need to shell escape single quotes (' becomes '"'"') because
|
35
59
|
# run_in_parallel wraps command in single quotes
|
36
|
-
%
|
60
|
+
%{/bin/bash -c '"'"'#{activate_pipefail} && (#{command}) | #{remove_ignored_lines}'"'"'}
|
37
61
|
else
|
38
62
|
command
|
39
63
|
end
|
40
64
|
end
|
41
65
|
|
66
|
+
def suppress_schema_load_output(command)
|
67
|
+
ParallelTests::Tasks.suppress_output(command, "^ ->\\|^-- ")
|
68
|
+
end
|
69
|
+
|
42
70
|
def check_for_pending_migrations
|
43
71
|
["db:abort_if_pending_migrations", "app:db:abort_if_pending_migrations"].each do |abort_migrations|
|
44
72
|
if Rake::Task.task_defined?(abort_migrations)
|
@@ -48,109 +76,147 @@ module ParallelTests
|
|
48
76
|
end
|
49
77
|
end
|
50
78
|
|
51
|
-
# parallel:spec[:count, :pattern, :options]
|
79
|
+
# parallel:spec[:count, :pattern, :options, :pass_through]
|
52
80
|
def parse_args(args)
|
53
81
|
# order as given by user
|
54
|
-
args = [args[:count], args[:pattern], args[:options]]
|
82
|
+
args = [args[:count], args[:pattern], args[:options], args[:pass_through]]
|
55
83
|
|
56
84
|
# count given or empty ?
|
57
85
|
# parallel:spec[2,models,options]
|
58
86
|
# parallel:spec[,models,options]
|
59
87
|
count = args.shift if args.first.to_s =~ /^\d*$/
|
60
|
-
num_processes = count.
|
88
|
+
num_processes = (count.to_s.empty? ? nil : Integer(count))
|
61
89
|
pattern = args.shift
|
62
90
|
options = args.shift
|
91
|
+
pass_through = args.shift
|
63
92
|
|
64
|
-
[num_processes, pattern.to_s, options.to_s]
|
93
|
+
[num_processes, pattern.to_s, options.to_s, pass_through.to_s]
|
65
94
|
end
|
66
95
|
end
|
67
96
|
end
|
68
97
|
end
|
69
98
|
|
70
99
|
namespace :parallel do
|
71
|
-
desc "
|
72
|
-
task :
|
73
|
-
ParallelTests::Tasks.
|
100
|
+
desc "Setup test databases via db:setup --> parallel:setup[num_cpus]"
|
101
|
+
task :setup, :count do |_, args|
|
102
|
+
command = "#{ParallelTests::Tasks.rake_bin} db:setup RAILS_ENV=#{ParallelTests::Tasks.rails_env}"
|
103
|
+
ParallelTests::Tasks.run_in_parallel(ParallelTests::Tasks.suppress_schema_load_output(command), args)
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "Create test databases via db:create --> parallel:create[num_cpus]"
|
107
|
+
task :create, :count do |_, args|
|
108
|
+
ParallelTests::Tasks.run_in_parallel(
|
109
|
+
"#{ParallelTests::Tasks.rake_bin} db:create RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
|
110
|
+
)
|
74
111
|
end
|
75
112
|
|
76
|
-
desc "
|
77
|
-
task :drop, :count do |
|
78
|
-
ParallelTests::Tasks.run_in_parallel(
|
113
|
+
desc "Drop test databases via db:drop --> parallel:drop[num_cpus]"
|
114
|
+
task :drop, :count do |_, args|
|
115
|
+
ParallelTests::Tasks.run_in_parallel(
|
116
|
+
"#{ParallelTests::Tasks.rake_bin} db:drop RAILS_ENV=#{ParallelTests::Tasks.rails_env} " \
|
117
|
+
"DISABLE_DATABASE_ENVIRONMENT_CHECK=1", args
|
118
|
+
)
|
79
119
|
end
|
80
120
|
|
81
|
-
desc "
|
82
|
-
task(:prepare, [:count]) do |
|
121
|
+
desc "Update test databases by dumping and loading --> parallel:prepare[num_cpus]"
|
122
|
+
task(:prepare, [:count]) do |_, args|
|
83
123
|
ParallelTests::Tasks.check_for_pending_migrations
|
84
|
-
if defined?(ActiveRecord) && ActiveRecord::Base.schema_format
|
85
|
-
# dump
|
86
|
-
|
87
|
-
|
124
|
+
if defined?(ActiveRecord::Base) && [:ruby, :sql].include?(ActiveRecord::Base.schema_format)
|
125
|
+
# fast: dump once, load in parallel
|
126
|
+
type =
|
127
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('6.1.0')
|
128
|
+
"schema"
|
129
|
+
else
|
130
|
+
ActiveRecord::Base.schema_format == :ruby ? "schema" : "structure"
|
131
|
+
end
|
132
|
+
|
133
|
+
Rake::Task["db:#{type}:dump"].invoke
|
134
|
+
|
135
|
+
# remove database connection to prevent "database is being accessed by other users"
|
136
|
+
ActiveRecord::Base.remove_connection if ActiveRecord::Base.configurations.any?
|
137
|
+
|
138
|
+
Rake::Task["parallel:load_#{type}"].invoke(args[:count])
|
88
139
|
else
|
89
|
-
#
|
90
|
-
args = args.to_hash.merge(:
|
91
|
-
|
92
|
-
ParallelTests::Tasks.run_in_parallel("
|
140
|
+
# slow: dump and load in in serial
|
141
|
+
args = args.to_hash.merge(non_parallel: true) # normal merge returns nil
|
142
|
+
task_name = Rake::Task.task_defined?('db:test:prepare') ? 'db:test:prepare' : 'app:db:test:prepare'
|
143
|
+
ParallelTests::Tasks.run_in_parallel("#{ParallelTests::Tasks.rake_bin} #{task_name}", args)
|
144
|
+
next
|
93
145
|
end
|
94
146
|
end
|
95
147
|
|
96
148
|
# when dumping/resetting takes too long
|
97
|
-
desc "
|
98
|
-
task :migrate, :count do |
|
99
|
-
ParallelTests::Tasks.run_in_parallel(
|
149
|
+
desc "Update test databases via db:migrate --> parallel:migrate[num_cpus]"
|
150
|
+
task :migrate, :count do |_, args|
|
151
|
+
ParallelTests::Tasks.run_in_parallel(
|
152
|
+
"#{ParallelTests::Tasks.rake_bin} db:migrate RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
desc "Rollback test databases via db:rollback --> parallel:rollback[num_cpus]"
|
157
|
+
task :rollback, :count do |_, args|
|
158
|
+
ParallelTests::Tasks.run_in_parallel(
|
159
|
+
"#{ParallelTests::Tasks.rake_bin} db:rollback RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
|
160
|
+
)
|
100
161
|
end
|
101
162
|
|
102
163
|
# just load the schema (good for integration server <-> no development db)
|
103
|
-
desc "
|
104
|
-
task :load_schema, :count do |
|
105
|
-
command = "
|
106
|
-
|
164
|
+
desc "Load dumped schema for test databases via db:schema:load --> parallel:load_schema[num_cpus]"
|
165
|
+
task :load_schema, :count do |_, args|
|
166
|
+
command = "#{ParallelTests::Tasks.rake_bin} #{ParallelTests::Tasks.purge_before_load} " \
|
167
|
+
"db:schema:load RAILS_ENV=#{ParallelTests::Tasks.rails_env} DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
|
168
|
+
ParallelTests::Tasks.run_in_parallel(ParallelTests::Tasks.suppress_schema_load_output(command), args)
|
107
169
|
end
|
108
170
|
|
109
171
|
# load the structure from the structure.sql file
|
110
|
-
|
111
|
-
|
112
|
-
|
172
|
+
# (faster for rails < 6.1, deprecated after and only configured by `ActiveRecord::Base.schema_format`)
|
173
|
+
desc "Load structure for test databases via db:schema:load --> parallel:load_structure[num_cpus]"
|
174
|
+
task :load_structure, :count do |_, args|
|
175
|
+
ParallelTests::Tasks.run_in_parallel(
|
176
|
+
"#{ParallelTests::Tasks.rake_bin} #{ParallelTests::Tasks.purge_before_load} " \
|
177
|
+
"db:structure:load RAILS_ENV=#{ParallelTests::Tasks.rails_env} DISABLE_DATABASE_ENVIRONMENT_CHECK=1", args
|
178
|
+
)
|
113
179
|
end
|
114
180
|
|
115
|
-
desc "
|
116
|
-
task :seed, :count do |
|
117
|
-
ParallelTests::Tasks.run_in_parallel(
|
181
|
+
desc "Load the seed data from db/seeds.rb via db:seed --> parallel:seed[num_cpus]"
|
182
|
+
task :seed, :count do |_, args|
|
183
|
+
ParallelTests::Tasks.run_in_parallel(
|
184
|
+
"#{ParallelTests::Tasks.rake_bin} db:seed RAILS_ENV=#{ParallelTests::Tasks.rails_env}", args
|
185
|
+
)
|
118
186
|
end
|
119
187
|
|
120
|
-
desc "
|
121
|
-
task :rake, :command do |
|
122
|
-
ParallelTests::Tasks.run_in_parallel(
|
188
|
+
desc "Launch given rake command in parallel"
|
189
|
+
task :rake, :command, :count do |_, args|
|
190
|
+
ParallelTests::Tasks.run_in_parallel(
|
191
|
+
"RAILS_ENV=#{ParallelTests::Tasks.rails_env} #{ParallelTests::Tasks.rake_bin} " \
|
192
|
+
"#{args.command}", args
|
193
|
+
)
|
123
194
|
end
|
124
195
|
|
125
196
|
['test', 'spec', 'features', 'features-spinach'].each do |type|
|
126
|
-
desc "
|
127
|
-
task type, [:count, :pattern, :options] do |
|
197
|
+
desc "Run #{type} in parallel with parallel:#{type}[num_cpus]"
|
198
|
+
task type, [:count, :pattern, :options, :pass_through] do |_t, args|
|
128
199
|
ParallelTests::Tasks.check_for_pending_migrations
|
200
|
+
ParallelTests::Tasks.load_lib
|
129
201
|
|
130
|
-
|
131
|
-
require "parallel_tests"
|
132
|
-
|
133
|
-
count, pattern, options = ParallelTests::Tasks.parse_args(args)
|
202
|
+
count, pattern, options, pass_through = ParallelTests::Tasks.parse_args(args)
|
134
203
|
test_framework = {
|
135
204
|
'spec' => 'rspec',
|
136
205
|
'test' => 'test',
|
137
206
|
'features' => 'cucumber',
|
138
|
-
'features-spinach' => 'spinach'
|
207
|
+
'features-spinach' => 'spinach'
|
139
208
|
}[type]
|
140
209
|
|
141
|
-
if test_framework == 'spinach'
|
142
|
-
|
143
|
-
end
|
210
|
+
type = 'features' if test_framework == 'spinach'
|
211
|
+
# Using the relative path to find the binary allow to run a specific version of it
|
144
212
|
executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')
|
145
213
|
|
146
|
-
command = "#{executable} #{type}
|
214
|
+
command = "#{ParallelTests.with_ruby_binary(Shellwords.escape(executable))} #{type} " \
|
215
|
+
"--type #{test_framework} " \
|
147
216
|
"-n #{count} " \
|
148
217
|
"--pattern '#{pattern}' " \
|
149
|
-
"--test-options '#{options}'"
|
150
|
-
|
151
|
-
ruby_binary = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
|
152
|
-
command = "#{ruby_binary} #{command}"
|
153
|
-
end
|
218
|
+
"--test-options '#{options}' " \
|
219
|
+
"#{pass_through}"
|
154
220
|
abort unless system(command) # allow to chain tasks e.g. rake parallel:spec parallel:features
|
155
221
|
end
|
156
222
|
end
|