mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00

This sets the HOMEBREW_NO_INSTALL_FROM_API environment variable to prevent the selected tests from using the API. We will need this as we transition to having the API be enabled by default when running the tests but it's also nice as a sanity check with the :needs_utils_curl scope in a few places.
330 lines
9.7 KiB
Ruby
330 lines
9.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
if ENV["HOMEBREW_TESTS_COVERAGE"]
|
|
require "simplecov"
|
|
require "simplecov-cobertura"
|
|
|
|
formatters = [
|
|
SimpleCov::Formatter::HTMLFormatter,
|
|
SimpleCov::Formatter::CoberturaFormatter,
|
|
]
|
|
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(formatters)
|
|
|
|
if RUBY_PLATFORM[/darwin/] && ENV["TEST_ENV_NUMBER"]
|
|
SimpleCov.at_exit do
|
|
result = SimpleCov.result
|
|
result.format! if ParallelTests.number_of_running_processes <= 1
|
|
end
|
|
end
|
|
end
|
|
|
|
require_relative "../warnings"
|
|
|
|
Warnings.ignore :parser_syntax do
|
|
require "rubocop"
|
|
end
|
|
|
|
require "rspec/github"
|
|
require "rspec/retry"
|
|
require "rspec/sorbet"
|
|
require "rubocop/rspec/support"
|
|
require "find"
|
|
require "byebug"
|
|
require "timeout"
|
|
|
|
$LOAD_PATH.unshift(File.expand_path("#{ENV.fetch("HOMEBREW_LIBRARY")}/Homebrew/test/support/lib"))
|
|
|
|
require_relative "support/extend/cachable"
|
|
|
|
require_relative "../global"
|
|
|
|
require "test/support/quiet_progress_formatter"
|
|
require "test/support/helper/cask"
|
|
require "test/support/helper/files"
|
|
require "test/support/helper/fixtures"
|
|
require "test/support/helper/formula"
|
|
require "test/support/helper/mktmpdir"
|
|
require "test/support/helper/output_as_tty"
|
|
|
|
require "test/support/helper/spec/shared_context/homebrew_cask" if OS.mac?
|
|
require "test/support/helper/spec/shared_context/integration_test"
|
|
require "test/support/helper/spec/shared_examples/formulae_exist"
|
|
|
|
TEST_DIRECTORIES = [
|
|
CoreTap.instance.path/"Formula",
|
|
HOMEBREW_CACHE,
|
|
HOMEBREW_CACHE_FORMULA,
|
|
HOMEBREW_CELLAR,
|
|
HOMEBREW_LOCKS,
|
|
HOMEBREW_LOGS,
|
|
HOMEBREW_TEMP,
|
|
].freeze
|
|
|
|
# Make `instance_double` and `class_double`
|
|
# work when type-checking is active.
|
|
RSpec::Sorbet.allow_doubles!
|
|
|
|
RSpec.configure do |config|
|
|
config.order = :random
|
|
|
|
config.raise_errors_for_deprecations!
|
|
config.warnings = true
|
|
config.disable_monkey_patching!
|
|
|
|
config.filter_run_when_matching :focus
|
|
|
|
config.silence_filter_announcements = true if ENV["TEST_ENV_NUMBER"]
|
|
|
|
# Improve backtrace formatting
|
|
config.filter_gems_from_backtrace "rspec-retry", "sorbet-runtime"
|
|
config.backtrace_exclusion_patterns << %r{test/spec_helper\.rb}
|
|
|
|
config.expect_with :rspec do |c|
|
|
c.max_formatted_output_length = 200
|
|
end
|
|
|
|
# Use rspec-retry to handle flaky tests.
|
|
config.default_sleep_interval = 1
|
|
|
|
# Don't want the nicer default retry behaviour when using BuildPulse to
|
|
# identify flaky tests.
|
|
config.default_retry_count = 2 unless ENV["BUILDPULSE"]
|
|
|
|
config.expect_with :rspec do |expectations|
|
|
# This option will default to `true` in RSpec 4. It makes the `description`
|
|
# and `failure_message` of custom matchers include text for helper methods
|
|
# defined using `chain`, e.g.:
|
|
# be_bigger_than(2).and_smaller_than(4).description
|
|
# # => "be bigger than 2 and smaller than 4"
|
|
# ...rather than:
|
|
# # => "be bigger than 2"
|
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
end
|
|
config.mock_with :rspec do |mocks|
|
|
# Prevents you from mocking or stubbing a method that does not exist on
|
|
# a real object. This is generally recommended, and will default to
|
|
# `true` in RSpec 4.
|
|
mocks.verify_partial_doubles = true
|
|
end
|
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
|
|
# Increase timeouts for integration tests (as we expect them to take longer).
|
|
config.around(:each, :integration_test) do |example|
|
|
example.metadata[:timeout] ||= 120
|
|
example.run
|
|
end
|
|
|
|
config.around(:each, :needs_network) do |example|
|
|
example.metadata[:timeout] ||= 120
|
|
|
|
# Don't want the nicer default retry behaviour when using BuildPulse to
|
|
# identify flaky tests.
|
|
example.metadata[:retry] ||= 4 unless ENV["BUILDPULSE"]
|
|
|
|
example.metadata[:retry_wait] ||= 2
|
|
example.metadata[:exponential_backoff] ||= true
|
|
example.run
|
|
end
|
|
|
|
# Never truncate output objects.
|
|
RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = nil
|
|
|
|
config.include(FileUtils)
|
|
|
|
config.include(Context)
|
|
|
|
config.include(RuboCop::RSpec::ExpectOffense)
|
|
|
|
config.include(Test::Helper::Cask)
|
|
config.include(Test::Helper::Fixtures)
|
|
config.include(Test::Helper::Formula)
|
|
config.include(Test::Helper::MkTmpDir)
|
|
config.include(Test::Helper::OutputAsTTY)
|
|
|
|
config.before(:each, :needs_linux) do
|
|
skip "Not running on Linux." unless OS.linux?
|
|
end
|
|
|
|
config.before(:each, :needs_macos) do
|
|
skip "Not running on macOS." unless OS.mac?
|
|
end
|
|
|
|
config.before(:each, :needs_ci) do
|
|
skip "Not running on CI." unless ENV["CI"]
|
|
end
|
|
|
|
config.before(:each, :needs_java) do
|
|
skip "Java is not installed." unless which("java")
|
|
end
|
|
|
|
config.before(:each, :needs_python) do
|
|
skip "Python is not installed." if !which("python3") && !which("python")
|
|
end
|
|
|
|
config.before(:each, :needs_network) do
|
|
skip "Requires network connection." unless ENV["HOMEBREW_TEST_ONLINE"]
|
|
end
|
|
|
|
config.before do |example|
|
|
next if example.metadata.key?(:needs_network)
|
|
next if example.metadata.key?(:needs_utils_curl)
|
|
|
|
allow(Utils::Curl).to receive(:curl_executable).and_raise(<<~ERROR)
|
|
Unexpected call to Utils::Curl.curl_executable without setting :needs_network or :needs_utils_curl.
|
|
ERROR
|
|
end
|
|
|
|
config.before(:each, :no_api) do
|
|
ENV["HOMEBREW_NO_INSTALL_FROM_API"] = "1"
|
|
end
|
|
|
|
config.before(:each, :needs_svn) do
|
|
svn_shim = HOMEBREW_SHIMS_PATH/"shared/svn"
|
|
skip "Subversion is not installed." unless quiet_system svn_shim, "--version"
|
|
|
|
svn_shim_path = Pathname(Utils.popen_read(svn_shim, "--homebrew=print-path").chomp.presence)
|
|
svn_paths = PATH.new(ENV.fetch("PATH"))
|
|
svn_paths.prepend(svn_shim_path.dirname)
|
|
|
|
if OS.mac?
|
|
xcrun_svn = Utils.popen_read("xcrun", "-f", "svn")
|
|
svn_paths.append(File.dirname(xcrun_svn)) if $CHILD_STATUS.success? && xcrun_svn.present?
|
|
end
|
|
|
|
svn = which("svn", svn_paths)
|
|
skip "svn is not installed." unless svn
|
|
|
|
svnadmin = which("svnadmin", svn_paths)
|
|
skip "svnadmin is not installed." unless svnadmin
|
|
|
|
ENV["PATH"] = PATH.new(ENV.fetch("PATH"))
|
|
.append(svn.dirname)
|
|
.append(svnadmin.dirname)
|
|
end
|
|
|
|
config.before(:each, :needs_homebrew_curl) do
|
|
ENV["HOMEBREW_CURL"] = HOMEBREW_BREWED_CURL_PATH
|
|
skip "A `curl` with TLS 1.3 support is required." unless Utils::Curl.curl_supports_tls13?
|
|
rescue FormulaUnavailableError
|
|
skip "No `curl` formula is available."
|
|
end
|
|
|
|
config.before(:each, :needs_unzip) do
|
|
skip "Unzip is not installed." unless which("unzip")
|
|
end
|
|
|
|
config.around do |example|
|
|
Homebrew.raise_deprecation_exceptions = true
|
|
|
|
Tap.installed.each(&:clear_cache)
|
|
Cachable::Registry.clear_all_caches
|
|
FormulaInstaller.clear_attempted
|
|
FormulaInstaller.clear_installed
|
|
FormulaInstaller.clear_fetched
|
|
Utils::Curl.clear_path_cache
|
|
|
|
TEST_DIRECTORIES.each(&:mkpath)
|
|
|
|
@__homebrew_failed = Homebrew.failed?
|
|
|
|
@__files_before_test = Test::Helper::Files.find_files
|
|
|
|
@__env = ENV.to_hash # dup doesn't work on ENV
|
|
|
|
@__stdout = $stdout.clone
|
|
@__stderr = $stderr.clone
|
|
@__stdin = $stdin.clone
|
|
|
|
begin
|
|
if !example.metadata.keys.intersect?([:focus, :byebug]) && !ENV.key?("HOMEBREW_VERBOSE_TESTS")
|
|
$stdout.reopen(File::NULL)
|
|
$stderr.reopen(File::NULL)
|
|
else
|
|
# don't retry when focusing/debugging
|
|
config.default_retry_count = 0
|
|
end
|
|
$stdin.reopen(File::NULL)
|
|
|
|
begin
|
|
timeout = example.metadata.fetch(:timeout, 60)
|
|
Timeout.timeout(timeout) do
|
|
example.run
|
|
end
|
|
rescue Timeout::Error => e
|
|
example.example.set_exception(e)
|
|
end
|
|
rescue SystemExit => e
|
|
example.example.set_exception(e)
|
|
ensure
|
|
ENV.replace(@__env)
|
|
Context.current = Context::ContextStruct.new
|
|
|
|
$stdout.reopen(@__stdout)
|
|
$stderr.reopen(@__stderr)
|
|
$stdin.reopen(@__stdin)
|
|
@__stdout.close
|
|
@__stderr.close
|
|
@__stdin.close
|
|
|
|
Tap.all.each(&:clear_cache)
|
|
Cachable::Registry.clear_all_caches
|
|
|
|
FileUtils.rm_rf [
|
|
*TEST_DIRECTORIES,
|
|
*Keg::MUST_EXIST_SUBDIRECTORIES,
|
|
HOMEBREW_LINKED_KEGS,
|
|
HOMEBREW_PINNED_KEGS,
|
|
HOMEBREW_PREFIX/"var",
|
|
HOMEBREW_PREFIX/"Caskroom",
|
|
HOMEBREW_PREFIX/"Frameworks",
|
|
HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-cask",
|
|
HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-bar",
|
|
HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-bundle",
|
|
HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-foo",
|
|
HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-services",
|
|
HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-shallow",
|
|
HOMEBREW_LIBRARY/"PinnedTaps",
|
|
HOMEBREW_REPOSITORY/".git",
|
|
CoreTap.instance.path/".git",
|
|
CoreTap.instance.alias_dir,
|
|
CoreTap.instance.path/"formula_renames.json",
|
|
CoreTap.instance.path/"tap_migrations.json",
|
|
CoreTap.instance.path/"audit_exceptions",
|
|
CoreTap.instance.path/"style_exceptions",
|
|
CoreTap.instance.path/"pypi_formula_mappings.json",
|
|
*Pathname.glob("#{HOMEBREW_CELLAR}/*/"),
|
|
]
|
|
|
|
files_after_test = Test::Helper::Files.find_files
|
|
|
|
diff = Set.new(@__files_before_test) ^ Set.new(files_after_test)
|
|
expect(diff).to be_empty, <<~EOS
|
|
file leak detected:
|
|
#{diff.map { |f| " #{f}" }.join("\n")}
|
|
EOS
|
|
|
|
Homebrew.failed = @__homebrew_failed
|
|
end
|
|
end
|
|
end
|
|
|
|
RSpec::Matchers.define_negated_matcher :not_to_output, :output
|
|
RSpec::Matchers.alias_matcher :have_failed, :be_failed
|
|
RSpec::Matchers.alias_matcher :a_string_containing, :include
|
|
|
|
RSpec::Matchers.define :a_json_string do
|
|
match do |actual|
|
|
JSON.parse(actual)
|
|
true
|
|
rescue JSON::ParserError
|
|
false
|
|
end
|
|
end
|
|
|
|
# Match consecutive elements in an array.
|
|
RSpec::Matchers.define :array_including_cons do |*cons|
|
|
match do |actual|
|
|
expect(actual.each_cons(cons.size)).to include(cons)
|
|
end
|
|
end
|