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

We already do this for deprecations but these may make warnings and errors from Homebrew easier to spot in GitHub Actions logs. While we're here, cleanup other cases that should have used `GitHub::Actions::Annotation` but didn't and provide some helpers and tweaks there necessary for our use case here.
302 lines
8.6 KiB
Ruby
302 lines
8.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe Kernel do
|
|
let(:dir) { mktmpdir }
|
|
|
|
def esc(code)
|
|
/(\e\[\d+m)*\e\[#{code}m/
|
|
end
|
|
|
|
describe "#ofail" do
|
|
it "sets Homebrew.failed to true" do
|
|
expect do
|
|
ofail "foo"
|
|
end.to output("Error: foo\n").to_stderr
|
|
|
|
expect(Homebrew).to have_failed
|
|
end
|
|
end
|
|
|
|
describe "#odie" do
|
|
it "exits with 1" do
|
|
expect do
|
|
odie "foo"
|
|
end.to output("Error: foo\n").to_stderr.and raise_error SystemExit
|
|
end
|
|
end
|
|
|
|
describe "#pretty_installed" do
|
|
subject(:pretty_installed_output) { pretty_installed("foo") }
|
|
|
|
context "when $stdout is a TTY" do
|
|
before { allow($stdout).to receive(:tty?).and_return(true) }
|
|
|
|
context "with HOMEBREW_NO_EMOJI unset" do
|
|
it "returns a string with a colored checkmark" do
|
|
expect(pretty_installed_output)
|
|
.to match(/#{esc 1}foo #{esc 32}✔#{esc 0}/)
|
|
end
|
|
end
|
|
|
|
context "with HOMEBREW_NO_EMOJI set" do
|
|
before { ENV["HOMEBREW_NO_EMOJI"] = "1" }
|
|
|
|
it "returns a string with colored info" do
|
|
expect(pretty_installed_output)
|
|
.to match(/#{esc 1}foo \(installed\)#{esc 0}/)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when $stdout is not a TTY" do
|
|
before { allow($stdout).to receive(:tty?).and_return(false) }
|
|
|
|
it "returns plain text" do
|
|
expect(pretty_installed_output).to eq("foo")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#pretty_uninstalled" do
|
|
subject(:pretty_uninstalled_output) { pretty_uninstalled("foo") }
|
|
|
|
context "when $stdout is a TTY" do
|
|
before { allow($stdout).to receive(:tty?).and_return(true) }
|
|
|
|
context "with HOMEBREW_NO_EMOJI unset" do
|
|
it "returns a string with a colored checkmark" do
|
|
expect(pretty_uninstalled_output)
|
|
.to match(/#{esc 1}foo #{esc 31}✘#{esc 0}/)
|
|
end
|
|
end
|
|
|
|
context "with HOMEBREW_NO_EMOJI set" do
|
|
before { ENV["HOMEBREW_NO_EMOJI"] = "1" }
|
|
|
|
it "returns a string with colored info" do
|
|
expect(pretty_uninstalled_output)
|
|
.to match(/#{esc 1}foo \(uninstalled\)#{esc 0}/)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when $stdout is not a TTY" do
|
|
before { allow($stdout).to receive(:tty?).and_return(false) }
|
|
|
|
it "returns plain text" do
|
|
expect(pretty_uninstalled_output).to eq("foo")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#interactive_shell" do
|
|
let(:shell) { dir/"myshell" }
|
|
|
|
it "starts an interactive shell session" do
|
|
File.write shell, <<~SH
|
|
#!/bin/sh
|
|
echo called > "#{dir}/called"
|
|
SH
|
|
|
|
FileUtils.chmod 0755, shell
|
|
|
|
ENV["SHELL"] = shell
|
|
|
|
expect { interactive_shell }.not_to raise_error
|
|
expect(dir/"called").to exist
|
|
end
|
|
end
|
|
|
|
describe "#with_custom_locale" do
|
|
it "temporarily overrides the system locale" do
|
|
ENV["LC_ALL"] = "en_US.UTF-8"
|
|
|
|
with_custom_locale("C") do
|
|
expect(ENV.fetch("LC_ALL")).to eq("C")
|
|
end
|
|
|
|
expect(ENV.fetch("LC_ALL")).to eq("en_US.UTF-8")
|
|
end
|
|
end
|
|
|
|
describe "#which" do
|
|
let(:cmd) { dir/"foo" }
|
|
|
|
before { FileUtils.touch cmd }
|
|
|
|
it "returns the first executable that is found" do
|
|
cmd.chmod 0744
|
|
expect(which(File.basename(cmd), File.dirname(cmd))).to eq(cmd)
|
|
end
|
|
|
|
it "skips non-executables" do
|
|
expect(which(File.basename(cmd), File.dirname(cmd))).to be_nil
|
|
end
|
|
|
|
it "skips malformed path and doesn't fail" do
|
|
# 'which' should not fail if a path is malformed
|
|
# see https://github.com/Homebrew/legacy-homebrew/issues/32789 for an example
|
|
cmd.chmod 0744
|
|
|
|
# ~~ will fail because ~foo resolves to foo's home and there is no '~' user
|
|
path = ["~~", File.dirname(cmd)].join(File::PATH_SEPARATOR)
|
|
expect(which(File.basename(cmd), path)).to eq(cmd)
|
|
end
|
|
end
|
|
|
|
describe "#which_all" do
|
|
let(:cmd_foo) { dir/"foo" }
|
|
let(:cmd_foo_bar) { dir/"bar/foo" }
|
|
let(:cmd_bar_baz_foo) { dir/"bar/baz/foo" }
|
|
|
|
before do
|
|
(dir/"bar/baz").mkpath
|
|
|
|
FileUtils.touch cmd_foo_bar
|
|
|
|
[cmd_foo, cmd_bar_baz_foo].each do |cmd|
|
|
FileUtils.touch cmd
|
|
cmd.chmod 0744
|
|
end
|
|
end
|
|
|
|
it "returns an array of all executables that are found" do
|
|
path = [
|
|
"#{dir}/bar/baz",
|
|
"#{dir}/baz:#{dir}",
|
|
"~baduserpath",
|
|
].join(File::PATH_SEPARATOR)
|
|
expect(which_all("foo", path)).to eq([cmd_bar_baz_foo, cmd_foo])
|
|
end
|
|
end
|
|
|
|
specify "#which_editor" do
|
|
ENV["HOMEBREW_EDITOR"] = "vemate -w"
|
|
ENV["HOMEBREW_PATH"] = dir
|
|
|
|
editor = "#{dir}/vemate"
|
|
FileUtils.touch editor
|
|
FileUtils.chmod 0755, editor
|
|
|
|
expect(which_editor).to eq("vemate -w")
|
|
end
|
|
|
|
describe "#pretty_duration" do
|
|
it "converts seconds to a human-readable string" do
|
|
expect(pretty_duration(1)).to eq("1 second")
|
|
expect(pretty_duration(2.5)).to eq("2 seconds")
|
|
expect(pretty_duration(42)).to eq("42 seconds")
|
|
expect(pretty_duration(240)).to eq("4 minutes")
|
|
expect(pretty_duration(252.45)).to eq("4 minutes 12 seconds")
|
|
end
|
|
end
|
|
|
|
specify "#disk_usage_readable" do
|
|
expect(disk_usage_readable(1)).to eq("1B")
|
|
expect(disk_usage_readable(1000)).to eq("1000B")
|
|
expect(disk_usage_readable(1024)).to eq("1KB")
|
|
expect(disk_usage_readable(1025)).to eq("1KB")
|
|
expect(disk_usage_readable(4_404_020)).to eq("4.2MB")
|
|
expect(disk_usage_readable(4_509_715_660)).to eq("4.2GB")
|
|
end
|
|
|
|
describe "#number_readable" do
|
|
it "returns a string with thousands separators" do
|
|
expect(number_readable(1)).to eq("1")
|
|
expect(number_readable(1_000)).to eq("1,000")
|
|
expect(number_readable(1_000_000)).to eq("1,000,000")
|
|
end
|
|
end
|
|
|
|
specify "#truncate_text_to_approximate_size" do
|
|
glue = "\n[...snip...]\n" # hard-coded copy from truncate_text_to_approximate_size
|
|
n = 20
|
|
long_s = "x" * 40
|
|
|
|
s = truncate_text_to_approximate_size(long_s, n)
|
|
expect(s.length).to eq(n)
|
|
expect(s).to match(/^x+#{Regexp.escape(glue)}x+$/)
|
|
|
|
s = truncate_text_to_approximate_size(long_s, n, front_weight: 0.0)
|
|
expect(s).to eq(glue + ("x" * (n - glue.length)))
|
|
|
|
s = truncate_text_to_approximate_size(long_s, n, front_weight: 1.0)
|
|
expect(s).to eq(("x" * (n - glue.length)) + glue)
|
|
end
|
|
|
|
describe "#odeprecated" do
|
|
it "raises a MethodDeprecatedError when `disable` is true" do
|
|
ENV.delete("HOMEBREW_DEVELOPER")
|
|
expect do
|
|
odeprecated(
|
|
"method", "replacement",
|
|
caller: ["#{HOMEBREW_LIBRARY}/Taps/playbrew/homebrew-play/"],
|
|
disable: true
|
|
)
|
|
end.to raise_error(
|
|
MethodDeprecatedError,
|
|
%r{method.*replacement.*playbrew/homebrew-play.*/Taps/playbrew/homebrew-play/}m,
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "#with_env" do
|
|
it "sets environment variables within the block" do
|
|
expect(ENV.fetch("PATH")).not_to eq("/bin")
|
|
with_env(PATH: "/bin") do
|
|
expect(ENV.fetch("PATH", nil)).to eq("/bin")
|
|
end
|
|
end
|
|
|
|
it "restores ENV after the block" do
|
|
with_env(PATH: "/bin") do
|
|
expect(ENV.fetch("PATH", nil)).to eq("/bin")
|
|
end
|
|
path = ENV.fetch("PATH", nil)
|
|
expect(path).not_to be_nil
|
|
expect(path).not_to eq("/bin")
|
|
end
|
|
|
|
it "restores ENV if an exception is raised" do
|
|
expect do
|
|
with_env(PATH: "/bin") do
|
|
raise StandardError, "boom"
|
|
end
|
|
end.to raise_error(StandardError)
|
|
|
|
path = ENV.fetch("PATH", nil)
|
|
expect(path).not_to be_nil
|
|
expect(path).not_to eq("/bin")
|
|
end
|
|
end
|
|
|
|
describe "#tap_and_name_comparison" do
|
|
describe "both strings are only names" do
|
|
it "alphabetizes the strings" do
|
|
expect(%w[a b].sort(&tap_and_name_comparison)).to eq(%w[a b])
|
|
expect(%w[b a].sort(&tap_and_name_comparison)).to eq(%w[a b])
|
|
end
|
|
end
|
|
|
|
describe "both strings include tap" do
|
|
it "alphabetizes the strings" do
|
|
expect(%w[a/z/z b/z/z].sort(&tap_and_name_comparison)).to eq(%w[a/z/z b/z/z])
|
|
expect(%w[b/z/z a/z/z].sort(&tap_and_name_comparison)).to eq(%w[a/z/z b/z/z])
|
|
|
|
expect(%w[z/a/z z/b/z].sort(&tap_and_name_comparison)).to eq(%w[z/a/z z/b/z])
|
|
expect(%w[z/b/z z/a/z].sort(&tap_and_name_comparison)).to eq(%w[z/a/z z/b/z])
|
|
|
|
expect(%w[z/z/a z/z/b].sort(&tap_and_name_comparison)).to eq(%w[z/z/a z/z/b])
|
|
expect(%w[z/z/b z/z/a].sort(&tap_and_name_comparison)).to eq(%w[z/z/a z/z/b])
|
|
end
|
|
end
|
|
|
|
describe "only one string includes tap" do
|
|
it "prefers the string without tap" do
|
|
expect(%w[a/z/z z].sort(&tap_and_name_comparison)).to eq(%w[z a/z/z])
|
|
expect(%w[z a/z/z].sort(&tap_and_name_comparison)).to eq(%w[z a/z/z])
|
|
end
|
|
end
|
|
end
|
|
end
|