parallel_tests 1.3.7 → 3.7.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|