-
Notifications
You must be signed in to change notification settings - Fork 500
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
module ParallelTests | ||
module Tasks | ||
module Helpers | ||
class << self | ||
def rails_env | ||
'test' | ||
end | ||
|
||
def load_lib | ||
$LOAD_PATH << File.expand_path('..', __dir__) | ||
require "parallel_tests" | ||
end | ||
|
||
def purge_before_load | ||
if Gem::Version.new(Rails.version) > Gem::Version.new('4.2.0') | ||
Rake::Task.task_defined?('db:purge') ? 'db:purge' : 'app:db:purge' | ||
end | ||
end | ||
|
||
def run_in_parallel(cmd, options = {}) | ||
load_lib | ||
|
||
# Using the relative path to find the binary allow to run a specific version of it | ||
executable = File.expand_path('../../bin/parallel_test', __dir__) | ||
command = ParallelTests.with_ruby_binary(executable) | ||
command += ['--exec', Shellwords.join(cmd)] | ||
command += ['-n', options[:count]] unless options[:count].to_s.empty? | ||
command << '--non-parallel' if options[:non_parallel] | ||
|
||
abort unless system(*command) | ||
end | ||
|
||
# this is a crazy-complex solution for a very simple problem: | ||
# removing certain lines from the output without changing the exit-status | ||
# normally I'd not do this, but it has been lots of fun and a great learning experience :) | ||
# | ||
# - sed does not support | without -r | ||
# - grep changes 0 exitstatus to 1 if nothing matches | ||
# - sed changes 1 exitstatus to 0 | ||
# - pipefail makes pipe fail with exitstatus of first failed command | ||
# - pipefail is not supported in (zsh) | ||
# - defining a new rake task like silence_schema would force users to load parallel_tests in test env | ||
# - simple system "set -o pipefail" returns nil even though set -o pipefail exists with 0 | ||
def suppress_output(command, ignore_regex) | ||
activate_pipefail = "set -o pipefail" | ||
remove_ignored_lines = %{(grep -v #{Shellwords.escape(ignore_regex)} || true)} | ||
|
||
# remove nil values (ex: #purge_before_load returns nil) | ||
command.compact! | ||
|
||
if system('/bin/bash', '-c', "#{activate_pipefail} 2>/dev/null") | ||
shell_command = "#{activate_pipefail} && (#{Shellwords.shelljoin(command)}) | #{remove_ignored_lines}" | ||
['/bin/bash', '-c', shell_command] | ||
else | ||
command | ||
end | ||
end | ||
|
||
def suppress_schema_load_output(command) | ||
suppress_output(command, "^ ->\\|^-- ") | ||
end | ||
|
||
def check_for_pending_migrations | ||
["db:abort_if_pending_migrations", "app:db:abort_if_pending_migrations"].each do |abort_migrations| | ||
if Rake::Task.task_defined?(abort_migrations) | ||
Rake::Task[abort_migrations].invoke | ||
break | ||
end | ||
end | ||
end | ||
|
||
# parallel:spec[:count, :pattern, :options, :pass_through] | ||
def parse_args(args) | ||
# order as given by user | ||
args = [args[:count], args[:pattern], args[:options], args[:pass_through]] | ||
|
||
# count given or empty ? | ||
# parallel:spec[2,models,options] | ||
# parallel:spec[,models,options] | ||
count = args.shift if args.first.to_s =~ /^\d*$/ | ||
num_processes = (count.to_s.empty? ? nil : Integer(count)) | ||
pattern = args.shift | ||
options = args.shift | ||
pass_through = args.shift | ||
|
||
[num_processes, pattern, options, pass_through] | ||
end | ||
|
||
def schema_format_based_on_rails_version | ||
if rails_7_or_greater? | ||
ActiveRecord.schema_format | ||
else | ||
ActiveRecord::Base.schema_format | ||
end | ||
end | ||
|
||
def schema_type_based_on_rails_version | ||
if rails_61_or_greater? || schema_format_based_on_rails_version == :ruby | ||
"schema" | ||
else | ||
"structure" | ||
end | ||
end | ||
|
||
def build_run_command(type, args) | ||
count, pattern, options, pass_through = parse_args(args) | ||
test_framework = { | ||
'spec' => 'rspec', | ||
'test' => 'test', | ||
'features' => 'cucumber', | ||
'features-spinach' => 'spinach' | ||
}.fetch(type) | ||
|
||
type = 'features' if test_framework == 'spinach' | ||
|
||
# Using the relative path to find the binary allow to run a specific version of it | ||
executable = File.expand_path('../../bin/parallel_test', __dir__) | ||
executable = ParallelTests.with_ruby_binary(executable) | ||
|
||
command = [*executable, type, '--type', test_framework] | ||
command += ['-n', count.to_s] if count | ||
command += ['--pattern', pattern] if pattern | ||
command += ['--test-options', options] if options | ||
command += Shellwords.shellsplit pass_through if pass_through | ||
command | ||
end | ||
|
||
def configured_databases | ||
return [] unless defined?(ActiveRecord) && rails_61_or_greater? | ||
|
||
@@configured_databases ||= ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml | ||
end | ||
|
||
def for_each_database(&block) | ||
# Use nil to represent all databases | ||
block&.call(nil) | ||
|
||
# skip if not rails or old rails version | ||
return if !defined?(ActiveRecord::Tasks::DatabaseTasks) || !ActiveRecord::Tasks::DatabaseTasks.respond_to?(:for_each) | ||
|
||
ActiveRecord::Tasks::DatabaseTasks.for_each(configured_databases) do |name| | ||
block&.call(name) | ||
end | ||
end | ||
|
||
private | ||
|
||
def rails_7_or_greater? | ||
Gem::Version.new(Rails.version) >= Gem::Version.new('7.0') | ||
end | ||
|
||
def rails_61_or_greater? | ||
Gem::Version.new(Rails.version) >= Gem::Version.new('6.1.0') | ||
end | ||
end | ||
end | ||
end | ||
end |