aboutsummaryrefslogtreecommitdiffstats
path: root/railties/test/isolation
diff options
context:
space:
mode:
authorMatthew Draper <matthew@trebex.net>2017-09-04 02:25:26 +0930
committerMatthew Draper <matthew@trebex.net>2017-09-04 20:19:39 +0930
commit802ce8a2392d3f749e665d4f9e54790cf613aaf9 (patch)
treec918e029aabc60a84afe7714030e35e81dffc0ec /railties/test/isolation
parent07bac9ef93d98a1e31cd5b2ce2aabc1e57816604 (diff)
downloadrails-802ce8a2392d3f749e665d4f9e54790cf613aaf9.tar.gz
rails-802ce8a2392d3f749e665d4f9e54790cf613aaf9.tar.bz2
rails-802ce8a2392d3f749e665d4f9e54790cf613aaf9.zip
Run in-app rails commands via fork+load where possible
While this avoids shell argument parsing, we still pass through everything in our stack.
Diffstat (limited to 'railties/test/isolation')
-rw-r--r--railties/test/isolation/abstract_unit.rb84
1 files changed, 81 insertions, 3 deletions
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 90c366ba17..a4c6fc4e68 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -236,10 +236,88 @@ module TestHelpers
end
end
- def script(script)
- Dir.chdir(app_path) do
- `#{Gem.ruby} #{app_path}/bin/rails #{script}`
+ # Invoke a bin/rails command inside the app
+ #
+ # allow_failures:: true to return normally if the command exits with
+ # a non-zero status. By default, this method will raise.
+ # stderr:: true to pass STDERR output straight to the "real" STDERR.
+ # By default, the STDERR and STDOUT of the process will be
+ # combined in the returned string.
+ # fork:: false to not use fork even when it's available. By default,
+ # when possible, the command is executed in a fork of the current
+ # process, avoiding the need to load core Rails libraries anew.
+ def rails(*args, allow_failure: false, stderr: false, fork: true)
+ args = args.flatten
+
+ command = "bin/rails #{Shellwords.join args}#{' 2>&1' unless stderr}"
+
+ # Don't fork if the environment has disabled it
+ fork = false if ENV["NO_FORK"]
+
+ # Don't fork if the runtime isn't able to
+ fork = false if !Process.respond_to?(:fork)
+
+ # Don't fork if we're re-invoking minitest
+ fork = false if args.first == "t" || args.grep(/\Atest(:|\z)/).any?
+
+ if fork
+ out_read, out_write = IO.pipe
+ if stderr
+ err_read, err_write = IO.pipe
+ else
+ err_write = out_write
+ end
+
+ pid = fork do
+ out_read.close
+ err_read.close if err_read
+
+ $stdin.reopen(File::NULL, "r")
+ $stdout.reopen(out_write)
+ $stderr.reopen(err_write)
+
+ at_exit do
+ case $!
+ when SystemExit
+ exit! $!.status
+ when nil
+ exit! 0
+ else
+ err_write.puts "#{$!.class}: #{$!}"
+ exit! 1
+ end
+ end
+
+ Rails.instance_variable_set :@_env, nil
+
+ $-v = $-w = false
+ Dir.chdir app_path unless Dir.pwd == app_path
+
+ ARGV.replace(args)
+ load "./bin/rails"
+
+ exit! 0
+ end
+
+ out_write.close
+
+ if err_read
+ err_write.close
+
+ $stderr.write err_read.read
+ end
+
+ output = out_read.read
+
+ Process.waitpid pid
+
+ else
+ output = `cd #{app_path}; #{command}`
end
+
+ raise "rails command failed (#{$?.exitstatus}): #{command}\n#{output}" unless allow_failure || $?.success?
+
+ output
end
def add_to_top_of_config(str)