mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
257 lines
11 KiB
Ruby
257 lines
11 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "bundle"
|
|
|
|
RSpec.describe Homebrew::Bundle::Commands::Cleanup do
|
|
describe "read Brewfile and current installation" do
|
|
before do
|
|
described_class.reset!
|
|
|
|
# don't try to load gcc/glibc
|
|
allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false)
|
|
|
|
allow_any_instance_of(Pathname).to receive(:read).and_return <<~EOS
|
|
tap 'x'
|
|
tap 'y'
|
|
cask '123'
|
|
brew 'a'
|
|
brew 'b'
|
|
brew 'd2'
|
|
brew 'homebrew/tap/f'
|
|
brew 'homebrew/tap/g'
|
|
brew 'homebrew/tap/h'
|
|
brew 'homebrew/tap/i2'
|
|
brew 'homebrew/tap/hasdependency'
|
|
brew 'hasbuilddependency1'
|
|
brew 'hasbuilddependency2'
|
|
mas 'appstoreapp1', id: 1
|
|
vscode 'VsCodeExtension1'
|
|
EOS
|
|
%w[a b d2 homebrew/tap/f homebrew/tap/g homebrew/tap/h homebrew/tap/i2
|
|
homebrew/tap/hasdependency hasbuilddependency1 hasbuilddependency2].each do |full_name|
|
|
tap_name, _, name = full_name.rpartition("/")
|
|
tap = tap_name.present? ? Tap.fetch(tap_name) : nil
|
|
f = formula(name, tap:) { url "#{name}-1.0" }
|
|
stub_formula_loader f, full_name
|
|
end
|
|
end
|
|
|
|
it "computes which casks to uninstall" do
|
|
allow(Homebrew::Bundle::CaskDumper).to receive(:casks).and_return(%w[123 456])
|
|
expect(described_class.casks_to_uninstall).to eql(%w[456])
|
|
end
|
|
|
|
it "computes which formulae to uninstall" do
|
|
dependencies_arrays_hash = { dependencies: [], build_dependencies: [] }
|
|
allow(Homebrew::Bundle::BrewDumper).to receive(:formulae).and_return [
|
|
{ name: "a2", full_name: "a2", aliases: ["a"], dependencies: ["d"] },
|
|
{ name: "c", full_name: "c" },
|
|
{ name: "d", full_name: "homebrew/tap/d", aliases: ["d2"] },
|
|
{ name: "e", full_name: "homebrew/tap/e" },
|
|
{ name: "f", full_name: "homebrew/tap/f" },
|
|
{ name: "h", full_name: "other/tap/h" },
|
|
{ name: "i", full_name: "homebrew/tap/i", aliases: ["i2"] },
|
|
{ name: "hasdependency", full_name: "homebrew/tap/hasdependency", dependencies: ["isdependency"] },
|
|
{ name: "isdependency", full_name: "homebrew/tap/isdependency" },
|
|
{
|
|
name: "hasbuilddependency1",
|
|
full_name: "hasbuilddependency1",
|
|
poured_from_bottle?: true,
|
|
build_dependencies: ["builddependency1"],
|
|
},
|
|
{
|
|
name: "hasbuilddependency2",
|
|
full_name: "hasbuilddependency2",
|
|
poured_from_bottle?: false,
|
|
build_dependencies: ["builddependency2"],
|
|
},
|
|
{ name: "builddependency1", full_name: "builddependency1" },
|
|
{ name: "builddependency2", full_name: "builddependency2" },
|
|
{ name: "caskdependency", full_name: "homebrew/tap/caskdependency" },
|
|
].map { |formula| dependencies_arrays_hash.merge(formula) }
|
|
allow(Homebrew::Bundle::CaskDumper).to receive(:formula_dependencies).and_return(%w[caskdependency])
|
|
expect(described_class.formulae_to_uninstall).to eql %w[
|
|
c
|
|
homebrew/tap/e
|
|
other/tap/h
|
|
builddependency1
|
|
]
|
|
end
|
|
|
|
it "computes which tap to untap" do
|
|
allow(Homebrew::Bundle::TapDumper).to \
|
|
receive(:tap_names).and_return(%w[z homebrew/bundle homebrew/core homebrew/tap])
|
|
expect(described_class.taps_to_untap).to eql(%w[z])
|
|
end
|
|
|
|
it "ignores unavailable formulae when computing which taps to keep" do
|
|
allow(Formulary).to \
|
|
receive(:factory).and_raise(TapFormulaUnavailableError.new(Tap.fetch("homebrew/tap"), "foo"))
|
|
allow(Homebrew::Bundle::TapDumper).to \
|
|
receive(:tap_names).and_return(%w[z homebrew/bundle homebrew/core homebrew/tap])
|
|
expect(described_class.taps_to_untap).to eql(%w[z homebrew/tap])
|
|
end
|
|
|
|
it "computes which VSCode extensions to uninstall" do
|
|
allow(Homebrew::Bundle::VscodeExtensionDumper).to receive(:extensions).and_return(%w[z])
|
|
expect(described_class.vscode_extensions_to_uninstall).to eql(%w[z])
|
|
end
|
|
|
|
it "computes which VSCode extensions to uninstall irrespective of case of the extension name" do
|
|
allow(Homebrew::Bundle::VscodeExtensionDumper).to receive(:extensions).and_return(%w[z vscodeextension1])
|
|
expect(described_class.vscode_extensions_to_uninstall).to eql(%w[z])
|
|
end
|
|
end
|
|
|
|
context "when there are no formulae to uninstall and no taps to untap" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: [],
|
|
formulae_to_uninstall: [],
|
|
taps_to_untap: [],
|
|
vscode_extensions_to_uninstall: [])
|
|
end
|
|
|
|
it "does nothing" do
|
|
expect(Kernel).not_to receive(:system)
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("")
|
|
described_class.run(force: true)
|
|
end
|
|
end
|
|
|
|
context "when there are casks to uninstall" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: %w[a b],
|
|
formulae_to_uninstall: [],
|
|
taps_to_untap: [],
|
|
vscode_extensions_to_uninstall: [])
|
|
end
|
|
|
|
it "uninstalls casks" do
|
|
expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "uninstall", "--cask", "--force", "a", "b")
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("")
|
|
expect { described_class.run(force: true) }.to output(/Uninstalled 2 casks/).to_stdout
|
|
end
|
|
end
|
|
|
|
context "when there are casks to zap" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: %w[a b],
|
|
formulae_to_uninstall: [],
|
|
taps_to_untap: [],
|
|
vscode_extensions_to_uninstall: [])
|
|
end
|
|
|
|
it "uninstalls casks" do
|
|
expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "uninstall", "--cask", "--zap", "--force", "a", "b")
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("")
|
|
expect { described_class.run(force: true, zap: true) }.to output(/Uninstalled 2 casks/).to_stdout
|
|
end
|
|
end
|
|
|
|
context "when there are formulae to uninstall" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: [],
|
|
formulae_to_uninstall: %w[a b],
|
|
taps_to_untap: [],
|
|
vscode_extensions_to_uninstall: [])
|
|
end
|
|
|
|
it "uninstalls formulae" do
|
|
expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "uninstall", "--formula", "--force", "a", "b")
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("")
|
|
expect { described_class.run(force: true) }.to output(/Uninstalled 2 formulae/).to_stdout
|
|
end
|
|
end
|
|
|
|
context "when there are taps to untap" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: [],
|
|
formulae_to_uninstall: [],
|
|
taps_to_untap: %w[a b],
|
|
vscode_extensions_to_uninstall: [])
|
|
end
|
|
|
|
it "untaps taps" do
|
|
expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "untap", "a", "b")
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("")
|
|
described_class.run(force: true)
|
|
end
|
|
end
|
|
|
|
context "when there are VSCode extensions to uninstall" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: [],
|
|
formulae_to_uninstall: [],
|
|
taps_to_untap: [],
|
|
vscode_extensions_to_uninstall: %w[GitHub.codespaces])
|
|
end
|
|
|
|
it "uninstalls extensions" do
|
|
expect(Kernel).to receive(:system).with("code", "--uninstall-extension", "GitHub.codespaces")
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("")
|
|
described_class.run(force: true)
|
|
end
|
|
end
|
|
|
|
context "when there are casks and formulae to uninstall and taps to untap but without passing `--force`" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: %w[a b],
|
|
formulae_to_uninstall: %w[a b],
|
|
taps_to_untap: %w[a b],
|
|
vscode_extensions_to_uninstall: %w[a b])
|
|
end
|
|
|
|
it "lists casks, formulae and taps" do
|
|
expect(Formatter).to receive(:columns).with(%w[a b]).exactly(4).times
|
|
expect(Kernel).not_to receive(:system)
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("")
|
|
expect do
|
|
described_class.run
|
|
end.to raise_error(SystemExit)
|
|
.and output(/Would uninstall formulae:.*Would untap:.*Would uninstall VSCode extensions:/m).to_stdout
|
|
end
|
|
end
|
|
|
|
context "when there is brew cleanup output" do
|
|
before do
|
|
described_class.reset!
|
|
allow(described_class).to receive_messages(casks_to_uninstall: [],
|
|
formulae_to_uninstall: [],
|
|
taps_to_untap: [],
|
|
vscode_extensions_to_uninstall: [])
|
|
end
|
|
|
|
def sane?
|
|
expect(described_class).to receive(:system_output_no_stderr).and_return("cleaned")
|
|
end
|
|
|
|
context "with --force" do
|
|
it "prints output" do
|
|
sane?
|
|
expect { described_class.run(force: true) }.to output(/cleaned/).to_stdout
|
|
end
|
|
end
|
|
|
|
context "without --force" do
|
|
it "prints output" do
|
|
sane?
|
|
expect { described_class.run }.to output(/cleaned/).to_stdout
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#system_output_no_stderr" do
|
|
it "shells out" do
|
|
expect(IO).to receive(:popen).and_return(StringIO.new("true"))
|
|
described_class.system_output_no_stderr("true")
|
|
end
|
|
end
|
|
end
|