Port Homebrew::DevCmd::Test

This commit is contained in:
Douglas Eichelberger 2024-03-21 21:52:32 -07:00
parent e0519d736a
commit 827e943803
3 changed files with 102 additions and 100 deletions

View File

@ -1,126 +1,127 @@
# typed: true # typed: true
# frozen_string_literal: true # frozen_string_literal: true
require "abstract_command"
require "extend/ENV" require "extend/ENV"
require "sandbox" require "sandbox"
require "timeout" require "timeout"
require "cli/parser" require "cli/parser"
module Homebrew module Homebrew
module_function module DevCmd
class Test < AbstractCommand
cmd_args do
description <<~EOS
Run the test method provided by an installed formula.
There is no standard output or return code, but generally it should notify the
user if something is wrong with the installed formula.
sig { returns(CLI::Parser) } *Example:* `brew install jruby && brew test jruby`
def test_args EOS
Homebrew::CLI::Parser.new do switch "-f", "--force",
description <<~EOS description: "Test formulae even if they are unlinked."
Run the test method provided by an installed formula. switch "--HEAD",
There is no standard output or return code, but generally it should notify the description: "Test the HEAD version of a formula."
user if something is wrong with the installed formula. switch "--keep-tmp",
description: "Retain the temporary files created for the test."
switch "--retry",
description: "Retry if a testing fails."
*Example:* `brew install jruby && brew test jruby` named_args :installed_formula, min: 1, without_api: true
EOS
switch "-f", "--force",
description: "Test formulae even if they are unlinked."
switch "--HEAD",
description: "Test the HEAD version of a formula."
switch "--keep-tmp",
description: "Retain the temporary files created for the test."
switch "--retry",
description: "Retry if a testing fails."
named_args :installed_formula, min: 1, without_api: true
end
end
def test
args = test_args.parse
Homebrew.install_bundler_gems!(groups: ["formula_test"], setup_path: false)
require "formula_assertions"
require "formula_free_port"
args.named.to_resolved_formulae.each do |f|
# Cannot test uninstalled formulae
unless f.latest_version_installed?
ofail "Testing requires the latest version of #{f.full_name}"
next
end end
# Cannot test formulae without a test method sig { override.void }
unless f.test_defined? def run
ofail "#{f.full_name} defines no test" Homebrew.install_bundler_gems!(groups: ["formula_test"], setup_path: false)
next
end
# Don't test unlinked formulae require "formula_assertions"
if !args.force? && !f.keg_only? && !f.linked? require "formula_free_port"
ofail "#{f.full_name} is not linked"
next
end
# Don't test formulae missing test dependencies args.named.to_resolved_formulae.each do |f|
missing_test_deps = f.recursive_dependencies do |dependent, dependency| # Cannot test uninstalled formulae
Dependency.prune if dependency.installed? unless f.latest_version_installed?
next if dependency.test? && dependent == f ofail "Testing requires the latest version of #{f.full_name}"
next
end
Dependency.prune unless dependency.required? # Cannot test formulae without a test method
end.map(&:to_s) unless f.test_defined?
unless missing_test_deps.empty? ofail "#{f.full_name} defines no test"
ofail "#{f.full_name} is missing test dependencies: #{missing_test_deps.join(" ")}" next
next end
end
oh1 "Testing #{f.full_name}" # Don't test unlinked formulae
if !args.force? && !f.keg_only? && !f.linked?
ofail "#{f.full_name} is not linked"
next
end
env = ENV.to_hash # Don't test formulae missing test dependencies
missing_test_deps = f.recursive_dependencies do |dependent, dependency|
Dependency.prune if dependency.installed?
next if dependency.test? && dependent == f
begin Dependency.prune unless dependency.required?
exec_args = HOMEBREW_RUBY_EXEC_ARGS + %W[ end.map(&:to_s)
-- unless missing_test_deps.empty?
#{HOMEBREW_LIBRARY_PATH}/test.rb ofail "#{f.full_name} is missing test dependencies: #{missing_test_deps.join(" ")}"
#{f.path} next
].concat(args.options_only) end
exec_args << "--HEAD" if f.head? oh1 "Testing #{f.full_name}"
Utils.safe_fork do env = ENV.to_hash
if Sandbox.available?
sandbox = Sandbox.new begin
f.logs.mkpath exec_args = HOMEBREW_RUBY_EXEC_ARGS + %W[
sandbox.record_log(f.logs/"test.sandbox.log") --
sandbox.allow_write_temp_and_cache #{HOMEBREW_LIBRARY_PATH}/test.rb
sandbox.allow_write_log(f) #{f.path}
sandbox.allow_write_xcode ].concat(args.options_only)
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/cache")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/homebrew/locks") exec_args << "--HEAD" if f.head?
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/log")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/run") Utils.safe_fork do
sandbox.exec(*exec_args) if Sandbox.available?
else sandbox = Sandbox.new
exec(*exec_args) f.logs.mkpath
sandbox.record_log(f.logs/"test.sandbox.log")
sandbox.allow_write_temp_and_cache
sandbox.allow_write_log(f)
sandbox.allow_write_xcode
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/cache")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/homebrew/locks")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/log")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/run")
sandbox.exec(*exec_args)
else
exec(*exec_args)
end
end
rescue Exception => e # rubocop:disable Lint/RescueException
retry if retry_test?(f, args:)
ofail "#{f.full_name}: failed"
$stderr.puts e, Utils::Backtrace.clean(e)
ensure
ENV.replace(env)
end end
end end
rescue Exception => e # rubocop:disable Lint/RescueException
retry if retry_test?(f, args:)
ofail "#{f.full_name}: failed"
$stderr.puts e, Utils::Backtrace.clean(e)
ensure
ENV.replace(env)
end end
end
end
def retry_test?(formula, args:) private
@test_failed ||= Set.new
if args.retry? && @test_failed.add?(formula) def retry_test?(formula, args:)
oh1 "Testing #{formula.full_name} (again)" @test_failed ||= Set.new
formula.clear_cache if args.retry? && @test_failed.add?(formula)
ENV["RUST_BACKTRACE"] = "full" oh1 "Testing #{formula.full_name} (again)"
true formula.clear_cache
else ENV["RUST_BACKTRACE"] = "full"
Homebrew.failed = true true
false else
Homebrew.failed = true
false
end
end
end end
end end
end end

View File

@ -18,7 +18,7 @@ require "dev-cmd/test"
TEST_TIMEOUT_SECONDS = 5 * 60 TEST_TIMEOUT_SECONDS = 5 * 60
begin begin
args = Homebrew.test_args.parse args = Homebrew::DevCmd::Test.new.args
Context.current = args.context Context.current = args.context
error_pipe = UNIXSocket.open(ENV.fetch("HOMEBREW_ERROR_PIPE"), &:recv_io) error_pipe = UNIXSocket.open(ENV.fetch("HOMEBREW_ERROR_PIPE"), &:recv_io)

View File

@ -1,8 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
require "cmd/shared_examples/args_parse" require "cmd/shared_examples/args_parse"
require "dev-cmd/test"
RSpec.describe "brew test" do RSpec.describe Homebrew::DevCmd::Test do
it_behaves_like "parseable arguments" it_behaves_like "parseable arguments"
it "tests a given Formula", :integration_test do it "tests a given Formula", :integration_test do