Port Homebrew::DevCmd::Test
This commit is contained in:
		
							parent
							
								
									e0519d736a
								
							
						
					
					
						commit
						827e943803
					
				@ -1,126 +1,127 @@
 | 
			
		||||
# typed: true
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "abstract_command"
 | 
			
		||||
require "extend/ENV"
 | 
			
		||||
require "sandbox"
 | 
			
		||||
require "timeout"
 | 
			
		||||
require "cli/parser"
 | 
			
		||||
 | 
			
		||||
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) }
 | 
			
		||||
  def test_args
 | 
			
		||||
    Homebrew::CLI::Parser.new 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.
 | 
			
		||||
          *Example:* `brew install jruby && brew test jruby`
 | 
			
		||||
        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."
 | 
			
		||||
 | 
			
		||||
        *Example:* `brew install jruby && brew test jruby`
 | 
			
		||||
      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
 | 
			
		||||
        named_args :installed_formula, min: 1, without_api: true
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Cannot test formulae without a test method
 | 
			
		||||
      unless f.test_defined?
 | 
			
		||||
        ofail "#{f.full_name} defines no test"
 | 
			
		||||
        next
 | 
			
		||||
      end
 | 
			
		||||
      sig { override.void }
 | 
			
		||||
      def run
 | 
			
		||||
        Homebrew.install_bundler_gems!(groups: ["formula_test"], setup_path: false)
 | 
			
		||||
 | 
			
		||||
      # Don't test unlinked formulae
 | 
			
		||||
      if !args.force? && !f.keg_only? && !f.linked?
 | 
			
		||||
        ofail "#{f.full_name} is not linked"
 | 
			
		||||
        next
 | 
			
		||||
      end
 | 
			
		||||
        require "formula_assertions"
 | 
			
		||||
        require "formula_free_port"
 | 
			
		||||
 | 
			
		||||
      # 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
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        Dependency.prune unless dependency.required?
 | 
			
		||||
      end.map(&:to_s)
 | 
			
		||||
      unless missing_test_deps.empty?
 | 
			
		||||
        ofail "#{f.full_name} is missing test dependencies: #{missing_test_deps.join(" ")}"
 | 
			
		||||
        next
 | 
			
		||||
      end
 | 
			
		||||
          # Cannot test formulae without a test method
 | 
			
		||||
          unless f.test_defined?
 | 
			
		||||
            ofail "#{f.full_name} defines no test"
 | 
			
		||||
            next
 | 
			
		||||
          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
 | 
			
		||||
        exec_args = HOMEBREW_RUBY_EXEC_ARGS + %W[
 | 
			
		||||
          --
 | 
			
		||||
          #{HOMEBREW_LIBRARY_PATH}/test.rb
 | 
			
		||||
          #{f.path}
 | 
			
		||||
        ].concat(args.options_only)
 | 
			
		||||
            Dependency.prune unless dependency.required?
 | 
			
		||||
          end.map(&:to_s)
 | 
			
		||||
          unless missing_test_deps.empty?
 | 
			
		||||
            ofail "#{f.full_name} is missing test dependencies: #{missing_test_deps.join(" ")}"
 | 
			
		||||
            next
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
        exec_args << "--HEAD" if f.head?
 | 
			
		||||
          oh1 "Testing #{f.full_name}"
 | 
			
		||||
 | 
			
		||||
        Utils.safe_fork do
 | 
			
		||||
          if Sandbox.available?
 | 
			
		||||
            sandbox = Sandbox.new
 | 
			
		||||
            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)
 | 
			
		||||
          env = ENV.to_hash
 | 
			
		||||
 | 
			
		||||
          begin
 | 
			
		||||
            exec_args = HOMEBREW_RUBY_EXEC_ARGS + %W[
 | 
			
		||||
              --
 | 
			
		||||
              #{HOMEBREW_LIBRARY_PATH}/test.rb
 | 
			
		||||
              #{f.path}
 | 
			
		||||
            ].concat(args.options_only)
 | 
			
		||||
 | 
			
		||||
            exec_args << "--HEAD" if f.head?
 | 
			
		||||
 | 
			
		||||
            Utils.safe_fork do
 | 
			
		||||
              if Sandbox.available?
 | 
			
		||||
                sandbox = Sandbox.new
 | 
			
		||||
                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
 | 
			
		||||
      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
 | 
			
		||||
 | 
			
		||||
  def retry_test?(formula, args:)
 | 
			
		||||
    @test_failed ||= Set.new
 | 
			
		||||
    if args.retry? && @test_failed.add?(formula)
 | 
			
		||||
      oh1 "Testing #{formula.full_name} (again)"
 | 
			
		||||
      formula.clear_cache
 | 
			
		||||
      ENV["RUST_BACKTRACE"] = "full"
 | 
			
		||||
      true
 | 
			
		||||
    else
 | 
			
		||||
      Homebrew.failed = true
 | 
			
		||||
      false
 | 
			
		||||
      private
 | 
			
		||||
 | 
			
		||||
      def retry_test?(formula, args:)
 | 
			
		||||
        @test_failed ||= Set.new
 | 
			
		||||
        if args.retry? && @test_failed.add?(formula)
 | 
			
		||||
          oh1 "Testing #{formula.full_name} (again)"
 | 
			
		||||
          formula.clear_cache
 | 
			
		||||
          ENV["RUST_BACKTRACE"] = "full"
 | 
			
		||||
          true
 | 
			
		||||
        else
 | 
			
		||||
          Homebrew.failed = true
 | 
			
		||||
          false
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ require "dev-cmd/test"
 | 
			
		||||
TEST_TIMEOUT_SECONDS = 5 * 60
 | 
			
		||||
 | 
			
		||||
begin
 | 
			
		||||
  args = Homebrew.test_args.parse
 | 
			
		||||
  args = Homebrew::DevCmd::Test.new.args
 | 
			
		||||
  Context.current = args.context
 | 
			
		||||
 | 
			
		||||
  error_pipe = UNIXSocket.open(ENV.fetch("HOMEBREW_ERROR_PIPE"), &:recv_io)
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
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 "tests a given Formula", :integration_test do
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user