# typed: false # frozen_string_literal: true require "cmd/info" require "cmd/shared_examples/args_parse" RSpec.describe Homebrew::Cmd::Info do before do allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false) end RSpec::Matchers.define :a_json_string do match do |actual| JSON.parse(actual) true rescue JSON::ParserError false end end def installed_info_formula test_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" end (HOMEBREW_CELLAR/"testball/0.1").mkpath test_formula end def installed_info_cask cask = Cask::Cask.new("local-transmission") do version "2.61" name "Transmission" desc "BitTorrent client" url "https://example.com/local-transmission.zip" end allow(cask).to receive(:installed_version).and_return("2.61") cask end it_behaves_like "parseable arguments" it "prints as json with the --json=v1 flag" do test_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" end info = described_class.new(["--json=v1", "testball"]) allow(info.args.named).to receive(:to_formulae).and_return([test_formula]) expect { info.run } .to output(a_json_string).to_stdout .and not_to_output.to_stderr end it "prints as json with the --json=v2 flag", :integration_test do setup_test_formula "testball" expect { brew "info", "testball", "--json=v2" } .to output(a_json_string).to_stdout .and not_to_output.to_stderr .and be_a_success end it "does not include installed casks in formula JSON" do formula = installed_info_formula allow(Formula).to receive(:installed).and_return([formula]) expect(Cask::Caskroom).not_to receive(:casks) output = +"" expect { described_class.new(["--json=v2", "--installed", "--formula"]).run } .to output(satisfy { |s| output << s true }).to_stdout .and not_to_output.to_stderr json = JSON.parse(output) expect(json["formulae"].length).to eq(1) expect(json["casks"]).to be_empty end it "does not include eval-all casks in formula JSON" do formula = installed_info_formula allow(Formula).to receive(:all).and_return([formula]) allow(Homebrew::EnvConfig).to receive(:tap_trust_configured?).and_return(true) expect(Cask::Cask).not_to receive(:all) output = +"" expect { described_class.new(["--json=v2", "--formula"]).run } .to output(satisfy { |s| output << s true }).to_stdout .and not_to_output.to_stderr json = JSON.parse(output) expect(json["formulae"].length).to eq(1) expect(json["casks"]).to be_empty end it "prints installed formulae in a human-readable inventory" do mktmpdir do |dir| tabfile = dir/AbstractTab::FILENAME tabfile.write("{}") formula = installed_info_formula allow(Formula).to receive(:installed).and_return([formula]) allow(Tab).to receive(:for_formula).with(formula).and_return( Tab.new(installed_on_request: true, source: { "tap" => "homebrew/core" }, tabfile:), ) allow(Cask::Caskroom).to receive(:casks).and_return([]) expected_output = <<~EOS ==> testball: Some test Formula from homebrew/core Installed: 0.1 (on request) EOS expect { described_class.new(["--installed"]).run } .to output(expected_output).to_stdout .and not_to_output.to_stderr end end it "prints installed casks in a human-readable inventory" do mktmpdir do |dir| tabfile = dir/AbstractTab::FILENAME tabfile.write("{}") cask = installed_info_cask allow(Formula).to receive(:installed).and_return([]) allow(Cask::Caskroom).to receive(:casks).and_return([cask]) allow(Cask::Tab).to receive(:for_cask).with(cask).and_return( Cask::Tab.new(installed_on_request: false, source: { "tap" => "homebrew/cask" }, tabfile:), ) expected_output = <<~EOS ==> local-transmission: (Transmission) BitTorrent client Cask from homebrew/cask Installed: 2.61 (dependency) EOS expect { described_class.new(["--installed"]).run } .to output(expected_output).to_stdout .and not_to_output.to_stderr end end it "omits missing cask descriptions from the installed inventory" do mktmpdir do |dir| tabfile = dir/AbstractTab::FILENAME tabfile.write("{}") cask = Cask::Cask.new("no-description") do version "1.0" name "No Description" url "https://example.com/no-description.zip" end allow(cask).to receive(:installed_version).and_return("1.0") allow(Formula).to receive(:installed).and_return([]) allow(Cask::Caskroom).to receive(:casks).and_return([cask]) allow(Cask::Tab).to receive(:for_cask).with(cask).and_return( Cask::Tab.new(source: { "tap" => "homebrew/cask" }, tabfile:), ) expected_output = <<~EOS ==> no-description Cask from homebrew/cask Installed: 1.0 EOS expect { described_class.new(["--installed"]).run } .to output(expected_output).to_stdout .and not_to_output.to_stderr end end it "omits install reason when receipt intent is unavailable" do mktmpdir do |dir| tabfile = dir/AbstractTab::FILENAME tabfile.write("{}") formula = installed_info_formula cask = installed_info_cask allow(Formula).to receive(:installed).and_return([formula]) allow(Tab).to receive(:for_formula).with(formula).and_return( Tab.new(source: { "tap" => "homebrew/core" }, tabfile:), ) allow(Cask::Caskroom).to receive(:casks).and_return([cask]) allow(Cask::Tab).to receive(:for_cask).with(cask).and_return( Cask::Tab.new(source: { "tap" => "homebrew/cask" }, tabfile:), ) expected_output = <<~EOS ==> testball: Some test Formula from homebrew/core Installed: 0.1 ==> local-transmission: (Transmission) BitTorrent client Cask from homebrew/cask Installed: 2.61 EOS expect { described_class.new(["--installed"]).run } .to output(expected_output).to_stdout .and not_to_output.to_stderr end end it "marks installed formulae in interactive inventory output" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) mktmpdir do |dir| tabfile = dir/AbstractTab::FILENAME tabfile.write("{}") formula = installed_info_formula allow(Formula).to receive(:installed).and_return([formula]) allow(Tab).to receive(:for_formula).with(formula).and_return( Tab.new(installed_on_request: true, source: { "tap" => "homebrew/core" }, tabfile:), ) allow(Cask::Caskroom).to receive(:casks).and_return([]) expect { described_class.new(["--installed"]).run } .to output(/testball .*✔.*: Some test/).to_stdout .and not_to_output.to_stderr end end it "prints verbose installed inventory as full info" do info = described_class.new(["--verbose", "--installed"]) formula = installed_info_formula cask = installed_info_cask allow(Formula).to receive(:installed).and_return([formula]) allow(Cask::Caskroom).to receive(:casks).and_return([cask]) expect(info).to receive(:info_formula).with(formula, shadowed_by: nil) expect(info).to receive(:info_cask).with(cask) expect { info.run } .to output("\n").to_stdout .and not_to_output.to_stderr end it "prints quiet formula information in the slim inventory format" do info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") expected_output = <<~EOS ==> testball: Some test Formula from https://example.com/testball.rb Not installed EOS expect { info.info_formula_summary(formula) } .to output(expected_output).to_stdout .and not_to_output.to_stderr end it "uses slim formula information when quiet is passed" do test_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" end info = described_class.new(["--quiet", "testball"]) allow(info.args.named).to receive(:to_formulae_and_casks_and_unavailable).and_return([test_formula]) expect(info).to receive(:info_formula_summary).with(test_formula) expect { info.run } .to not_to_output.to_stderr end it "prints inline summary information for formulae" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" option "with-foo", "Build with foo" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Installs from source: yes/).to_stdout .and not_to_output(/Metadata/).to_stdout .and not_to_output(/supports macOS and Linux/).to_stdout .and not_to_output.to_stderr end it "shows a conflict by its resolved full name" do info = described_class.new([]) formula = formula("testball") do url "https://brew.sh/testball-0.1.tar.gz" conflicts_with "other" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") other = formula("other") { url "https://brew.sh/other-0.1.tar.gz" } allow(other).to receive(:full_name).and_return("someuser/tap/other") allow(Formulary).to receive(:factory).with("other").and_return(other) expect { info.info_formula(formula) } .to output(%r{Conflicts with:\n someuser/tap/other}).to_stdout end it "omits a stale conflict that resolves to the formula itself" do info = described_class.new([]) formula = formula("testball") do url "https://brew.sh/testball-0.1.tar.gz" conflicts_with "testball" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(Formulary).to receive(:factory).with("testball").and_return(formula) expect { info.info_formula(formula) } .not_to output(/Conflicts with:/).to_stdout end it "marks a deprecated formula with `(deprecated)` in the title" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" deprecate! date: "2024-01-01", because: :versioned_formula end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/==> .*testball.*\(deprecated\):/).to_stdout .and not_to_output.to_stderr end it "marks a disabled formula with `(disabled)` in the title" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" disable! date: "2024-01-01", because: :unmaintained end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/==> .*testball.*\(disabled\):/).to_stdout .and not_to_output.to_stderr end it "shows separate blocks for an unqualified and a qualified input that resolve to the same shadowed formula" do info = described_class.new([]) core = installed_info_formula keg_path = HOMEBREW_CELLAR/"testball/0.1" tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.source["tap"] = "ataraxy-labs/tap" tab.write allow(core).to receive(:tap).and_return(Tap.fetch("homebrew/core")) installed = formula("testball") { url "https://brew.sh/testball-0.1.tar.gz" } allow(installed).to receive_messages(tap: Tap.fetch("ataraxy-labs/tap"), full_name: "ataraxy-labs/tap/testball") allow(Formulary).to receive(:factory).with("ataraxy-labs/tap/testball").and_return(installed) allow(info).to receive(:github_info).and_return("https://example.com/testball.rb") allow(info.args.named).to receive_messages( downcased_unique_named: ["testball", "homebrew/core/testball"], to_formulae_and_casks_and_unavailable: [core, core], ) expect { info.print_info } .to output(%r{ataraxy-labs/tap/testball.*homebrew/core/testball.*Not installed}m).to_stdout end it "reports an unavailable name without raising" do info = described_class.new([]) error = FormulaOrCaskUnavailableError.new("nonexistent-formula") allow(info.args.named).to receive_messages( downcased_unique_named: ["nonexistent-formula"], to_formulae_and_casks_and_unavailable: [error], ) expect { info.print_info } .to output(/No available formula or cask with the name "nonexistent-formula"/).to_stderr end it "qualifies the name, reports not installed and shows the shadowing keg when the keg belongs to another tap" do info = described_class.new([]) formula = installed_info_formula keg_path = HOMEBREW_CELLAR/"testball/0.1" tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.source["tap"] = "ataraxy-labs/tap" tab.write allow(formula).to receive(:tap).and_return(Tap.fetch("homebrew/core")) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") shadowing = formula("testball") { url "https://brew.sh/testball-0.1.tar.gz" } allow(shadowing).to receive_messages(tap: Tap.fetch("ataraxy-labs/tap"), full_name: "ataraxy-labs/tap/testball") allow(Formulary).to receive(:factory).with("ataraxy-labs/tap/testball").and_return(shadowing) expect { info.info_formula(formula) } .to output(%r{homebrew/core/testball.*Not installed.*ataraxy-labs/tap/testball}m).to_stdout end it "reloads the formula from the install receipt's tap and reports the shadowing tap" do info = described_class.new([]) formula = installed_info_formula keg_path = HOMEBREW_CELLAR/"testball/0.1" tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.source["tap"] = "ataraxy-labs/tap" tab.write shadowing_tap = Tap.fetch("homebrew/core") allow(formula).to receive(:tap).and_return(shadowing_tap) keg_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" end allow(Formulary).to receive(:factory).with("ataraxy-labs/tap/testball").and_return(keg_formula) expect(info.installed_resolution(formula)).to eq([keg_formula, shadowing_tap]) end it "resolves the keg's own name when it differs from the formula (installed via alias)" do info = described_class.new([]) formula = installed_info_formula tab = instance_double(Tab, tap: Tap.fetch("stripe/stripe-cli")) keg = instance_double(Keg, name: "stripe", tab:) allow(formula).to receive_messages(tap: Tap.fetch("homebrew/core"), installed_kegs: [keg]) keg_formula = formula("stripe") { url "https://brew.sh/stripe-1.0.tar.gz" } allow(Formulary).to receive(:factory).with("stripe/stripe-cli/stripe").and_return(keg_formula) expect(info.installed_resolution(formula)).to eq([keg_formula, Tap.fetch("homebrew/core")]) end it "returns the original formula and no shadowing tap when the install receipt has no tap" do info = described_class.new([]) formula = installed_info_formula keg_path = HOMEBREW_CELLAR/"testball/0.1" tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write expect(info.installed_resolution(formula)).to eq([formula, nil]) end it "returns the original formula and no shadowing tap when the install receipt's tap matches" do info = described_class.new([]) formula = installed_info_formula keg_path = HOMEBREW_CELLAR/"testball/0.1" tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.source["tap"] = "homebrew/core" tab.write allow(formula).to receive(:tap).and_return(Tap.fetch("homebrew/core")) expect(info.installed_resolution(formula)).to eq([formula, nil]) end it "warns about a shadowing tap when info_formula is given one" do info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula, shadowed_by: Tap.fetch("homebrew/core")) } .to output(%r{Warning: `testball` shadows `homebrew/core/testball`}).to_stdout .and not_to_output.to_stderr end it "treats a `tap/name` input as user-qualified" do info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" end allow(formula).to receive(:tap).and_return(Tap.fetch("homebrew/core")) qualified = Set["homebrew/core/testball"] expect(info.formula_qualified_by_user?(formula, qualified)).to be(true) end it "treats a bare unqualified input as not user-qualified" do info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" end expect(info.formula_qualified_by_user?(formula, Set.new)).to be(false) end it "--json swaps an unqualified-input formula to its installed tap" do info = described_class.new(["--json", "testball"]) shadowed_formula = installed_info_formula keg_path = HOMEBREW_CELLAR/"testball/0.1" tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.source["tap"] = "ataraxy-labs/tap" tab.write allow(shadowed_formula).to receive(:tap).and_return(Tap.fetch("homebrew/core")) installed_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" end allow(installed_formula).to receive(:tap).and_return(Tap.fetch("ataraxy-labs/tap")) allow(info.args.named).to receive(:to_formulae).and_return([shadowed_formula]) allow(Formulary).to receive(:factory).with("ataraxy-labs/tap/testball").and_return(installed_formula) output = +"" expect { info.run }.to output(satisfy { |s| output << s true }).to_stdout expect(JSON.parse(output).first["tap"]).to eq("ataraxy-labs/tap") end it "--json honours a tap-qualified input without swapping" do info = described_class.new(["--json", "homebrew/core/testball"]) formula = installed_info_formula keg_path = HOMEBREW_CELLAR/"testball/0.1" tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.source["tap"] = "ataraxy-labs/tap" tab.write allow(formula).to receive(:tap).and_return(Tap.fetch("homebrew/core")) allow(info.args.named).to receive(:to_formulae).and_return([formula]) output = +"" expect { info.run }.to output(satisfy { |s| output << s true }).to_stdout expect(JSON.parse(output).first["tap"]).to eq("homebrew/core") end it "prints required, recursive runtime, and dependent counts in the dependencies section" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" depends_on "bar" end direct_dependency = formula.deps.required.first # Simulate an installed keg with tab runtime dependencies keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.runtime_dependencies = [ { "full_name" => "installed-dep", "version" => "1.0" }, { "full_name" => "missing-dep", "version" => "2.0" }, ] tab.write # Create a rack for the installed dependency installed_dep_path = HOMEBREW_CELLAR/"installed-dep/1.0" installed_dep_path.mkpath installed_dep_tab = Tab.empty installed_dep_tab.tabfile = installed_dep_path/AbstractTab::FILENAME installed_dep_tab.write # Create a dependent keg whose tab references testball dependent_keg_path = HOMEBREW_CELLAR/"some-dependent/1.0" dependent_keg_path.mkpath dependent_tab = Tab.empty dependent_tab.tabfile = dependent_keg_path/AbstractTab::FILENAME dependent_tab.runtime_dependencies = [ { "full_name" => "testball", "version" => "0.1" }, ] dependent_tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) allow(direct_dependency).to receive(:satisfied?).and_return(true) expected_output = Regexp.new( "==> Dependencies\nRequired \\(1\\): .*bar.*\n" \ "Recursive Runtime \\(2\\): 1 installed .*✔, 1 missing .*✘\nDependents: 1", ) expect { info.info_formula(formula) } .to output(expected_output).to_stdout .and not_to_output(/^Dependencies: /).to_stdout .and not_to_output.to_stderr end it "lists installed dependents inline under Dependencies with --verbose" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new(["--verbose"]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write %w[some-dependent another-dependent].each do |dependent_name| dependent_keg_path = HOMEBREW_CELLAR/"#{dependent_name}/1.0" dependent_keg_path.mkpath dependent_tab = Tab.empty dependent_tab.tabfile = dependent_keg_path/AbstractTab::FILENAME dependent_tab.runtime_dependencies = [ { "full_name" => "testball", "version" => "0.1" }, ] dependent_tab.write end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/^Dependents \(2\): another-dependent, some-dependent$/).to_stdout .and not_to_output(/^Dependents: /).to_stdout .and not_to_output.to_stderr end it "summarises recursive runtime dependencies as all installed when none are missing" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" depends_on "bar" end direct_dependency = formula.deps.required.first keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.runtime_dependencies = [{ "full_name" => "installed-dep", "version" => "1.0" }] tab.write installed_dep_path = HOMEBREW_CELLAR/"installed-dep/1.0" installed_dep_path.mkpath installed_dep_tab = Tab.empty installed_dep_tab.tabfile = installed_dep_path/AbstractTab::FILENAME installed_dep_tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) allow(direct_dependency).to receive(:satisfied?).and_return(true) expect { info.info_formula(formula) } .to output(/Recursive Runtime \(1\): all installed .*✔/).to_stdout .and not_to_output(/missing/).to_stdout .and not_to_output.to_stderr end it "marks a tab-listed dep with no installed rack as unsatisfied" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.runtime_dependencies = [{ "full_name" => "bar", "version" => "1.0", "pkg_version" => "1.0" }] tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*bar.*✘/).to_stdout .and not_to_output.to_stderr end it "marks a tab-listed dep with an installed rack as satisfied when the dep formula is not outdated" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end direct_dependency = formula.deps.required.first keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.runtime_dependencies = [{ "full_name" => "bar", "version" => "1.0", "pkg_version" => "1.0" }] tab.write bar_keg_path = HOMEBREW_CELLAR/"bar/1.0" bar_keg_path.mkpath bar_tab = Tab.empty bar_tab.tabfile = bar_keg_path/AbstractTab::FILENAME bar_tab.write bar_formula = instance_double(Formula, outdated?: false) allow(direct_dependency).to receive(:to_formula).and_return(bar_formula) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*bar.*✔/).to_stdout .and not_to_output.to_stderr end it "marks a tab-listed dep with an installed rack as outdated when the dep formula is outdated" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end direct_dependency = formula.deps.required.first keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.runtime_dependencies = [{ "full_name" => "bar", "version" => "1.0", "pkg_version" => "1.0" }] tab.write bar_keg_path = HOMEBREW_CELLAR/"bar/1.0" bar_keg_path.mkpath bar_tab = Tab.empty bar_tab.tabfile = bar_keg_path/AbstractTab::FILENAME bar_tab.write bar_formula = instance_double(Formula, outdated?: true) allow(direct_dependency).to receive(:to_formula).and_return(bar_formula) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*bar.*↑/).to_stdout .and not_to_output.to_stderr end it "marks an installed dep on an uninstalled formula as satisfied" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end direct_dependency = formula.deps.required.first bar_keg_path = HOMEBREW_CELLAR/"bar/1.0" bar_keg_path.mkpath bar_tab = Tab.empty bar_tab.tabfile = bar_keg_path/AbstractTab::FILENAME bar_tab.write bar_formula = instance_double(Formula, outdated?: false) allow(direct_dependency).to receive(:to_formula).and_return(bar_formula) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*bar.*✔/).to_stdout .and not_to_output.to_stderr end it "marks an outdated installed dep on an uninstalled formula as upgradable" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end direct_dependency = formula.deps.required.first bar_keg_path = HOMEBREW_CELLAR/"bar/1.0" bar_keg_path.mkpath bar_tab = Tab.empty bar_tab.tabfile = bar_keg_path/AbstractTab::FILENAME bar_tab.write bar_formula = instance_double(Formula, outdated?: true) allow(direct_dependency).to receive(:to_formula).and_return(bar_formula) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*bar.*↑/).to_stdout .and not_to_output.to_stderr end it "marks an aliased dep as installed when the underlying rack exists under a different name" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "pkg-config" end direct_dependency = formula.deps.required.first pkgconf_keg_path = HOMEBREW_CELLAR/"pkgconf/2.5.1" pkgconf_keg_path.mkpath pkgconf_tab = Tab.empty pkgconf_tab.tabfile = pkgconf_keg_path/AbstractTab::FILENAME pkgconf_tab.write pkgconf_formula = instance_double(Formula, any_version_installed?: true, outdated?: false) allow(direct_dependency).to receive(:to_formula).and_return(pkgconf_formula) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*pkg-config.*✔/).to_stdout .and not_to_output.to_stderr end it "does not mark a missing dep on an uninstalled formula" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): bar\n/).to_stdout .and not_to_output.to_stderr end it "marks a dep absent from the installed keg's tab as unsatisfied when its rack is also missing" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.runtime_dependencies = [] tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*bar.*✘/).to_stdout .and not_to_output.to_stderr end it "marks a dep absent from the installed keg's tab as installed when its rack exists" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" desc "Some test" depends_on "bar" end keg_path = HOMEBREW_CELLAR/"testball/0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.runtime_dependencies = [] tab.write bar_keg_path = HOMEBREW_CELLAR/"bar/1.0" bar_keg_path.mkpath bar_tab = Tab.empty bar_tab.tabfile = bar_keg_path/AbstractTab::FILENAME bar_tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(/Required \(1\): .*bar.*↑/).to_stdout .and not_to_output.to_stderr end it "omits build dependencies when a formula would pour from a bottle" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" depends_on "bar" => :build end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages( core_formula?: false, recursive_dependencies: [], stable: instance_double( SoftwareSpec, version: Version.new("0.1"), bottled?: true, ), pour_bottle?: true, ) expect { info.info_formula(formula) } .to not_to_output(/Build \(1\): .*bar.*/).to_stdout .and not_to_output(/==> Dependencies/).to_stdout .and not_to_output.to_stderr end it "shows the installed and stable versions in the headline when outdated" do info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, outdated?: true) keg_path = HOMEBREW_CELLAR/"testball/0.0.1" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write expect { info.info_formula(formula) } .to output(/\A==> testball: 0\.0\.1 → stable 0\.1\n/).to_stdout .and not_to_output.to_stderr end it "prints Linux requirements through the requirements section" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages( core_formula?: false, requirements: Requirements.new(LinuxRequirement.new), ) expect { info.info_formula(formula) } .to output(/Requirements\nRequired: .*Linux/).to_stdout .and not_to_output(/supports Linux/).to_stdout .and not_to_output.to_stderr end it "hides source install metadata for formulae that only run on another OS" do allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end os_requirement = OS.mac? ? LinuxRequirement.new : MacOSRequirement.new([:sonoma]) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages( core_formula?: false, requirements: Requirements.new(os_requirement), ) expect { info.info_formula(formula) } .to not_to_output(/Installs from source: yes/).to_stdout .and not_to_output.to_stderr end it "prints a Binaries section listing executables in bin and sbin with --verbose" do info = described_class.new(["--verbose"]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end keg_path = HOMEBREW_CELLAR/"testball/0.1" (keg_path/"bin").mkpath (keg_path/"sbin").mkpath ["bin/testball", "bin/another", "sbin/daemon"].each do |rel| file = keg_path/rel file.write("#!/bin/sh\n") file.chmod(0755) end tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to output(a_string_including("==> Binaries\nanother\ndaemon\ntestball\n")).to_stdout .and not_to_output.to_stderr end it "prints a Binaries section from the bottle manifest when the formula is not installed with --verbose" do info = described_class.new(["--verbose"]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end bottle = instance_double( Bottle, path_exec_files: ["bin/testball", "bin/another", "sbin/daemon"], bottle_size: nil, installed_size: nil, ) allow(bottle).to receive(:fetch_tab) allow(formula).to receive_messages(bottle:, core_formula?: false) allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") expect { info.info_formula(formula) } .to output(a_string_including("==> Binaries\nanother\ndaemon\ntestball\n")).to_stdout .and not_to_output.to_stderr end it "omits the Binaries section without --verbose" do info = described_class.new([]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end keg_path = HOMEBREW_CELLAR/"testball/0.1" (keg_path/"bin").mkpath binary = keg_path/"bin/testball" binary.write("#!/bin/sh\n") binary.chmod(0755) tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to not_to_output(/==> Binaries/).to_stdout .and not_to_output.to_stderr end it "omits the Binaries section when no executables are installed" do info = described_class.new(["--verbose"]) formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" homepage "https://brew.sh/testball" desc "Some test" end keg_path = HOMEBREW_CELLAR/"testball/0.1" (keg_path/"lib").mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb") allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new]) expect { info.info_formula(formula) } .to not_to_output(/==> Binaries/).to_stdout .and not_to_output.to_stderr end describe "::installation_status" do it "prints on-request installs explicitly" do expect(described_class.installation_status(instance_double(Tab, installed_on_request: true))) .to eq("Installed (on request)") end it "treats non-requested installs as dependency installs" do expect(described_class.installation_status(instance_double(Tab, installed_on_request: false))) .to eq("Installed (as dependency)") end end describe "::metadata_lines" do before { allow($stdout).to receive(:tty?).and_return(true) } it "returns summary lines for pinned formulae" do test_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0" end allow(test_formula).to receive_messages(any_version_installed?: true, pinned?: true, pinned_version: "1.0") mktmpdir do |dir| pin_path = Pathname(dir/"testball") pin_path.write("pin") pin_time = Time.at(1_720_189_900) File.utime(pin_time, pin_time, pin_path) allow(FormulaPin).to receive(:new).with(test_formula).and_return(instance_double(FormulaPin, path: pin_path)) expect(described_class.metadata_lines(test_formula)).to eq([ "Pinned: 1.0 on #{pin_time.strftime("%Y-%m-%d at %H:%M:%S")}", ]) end end it "returns summary lines for pinned casks" do cask = Cask::Cask.new("test-cask") do version "1.0" url "https://brew.sh/test-cask.zip" end allow(cask).to receive_messages(pinned?: true, pinned_version: "1.0") mktmpdir do |dir| pin_path = Pathname(dir/"test-cask") pin_path.write("pin") pin_time = Time.at(1_720_189_900) File.utime(pin_time, pin_time, pin_path) allow(cask).to receive(:pin_path).and_return(pin_path) expect(described_class.metadata_lines(cask)).to eq([ "Pinned: 1.0 on #{pin_time.strftime("%Y-%m-%d at %H:%M:%S")}", ]) end end end describe "::github_remote_path" do let(:remote) { "https://github.com/Homebrew/homebrew-core" } specify "returns correct URLs" do expect(described_class.new([]).github_remote_path(remote, "Formula/git.rb")) .to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/git.rb") expect(described_class.new([]).github_remote_path("#{remote}.git", "Formula/git.rb")) .to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/git.rb") expect(described_class.new([]).github_remote_path("git@github.com:user/repo", "foo.rb")) .to eq("https://github.com/user/repo/blob/HEAD/foo.rb") expect(described_class.new([]).github_remote_path("https://mywebsite.com", "foo/bar.rb")) .to eq("https://mywebsite.com/foo/bar.rb") end end describe "Aliases and Old Names rows" do it "lists aliases on their own row when the formula has any" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end allow(main_formula).to receive_messages(aliases: ["testball@1.0", "tball", "googleball"], oldnames: []) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(/^Aliases: testball@1\.0, tball, googleball$/).to_stdout .and not_to_output(/^Old Names:/).to_stdout .and not_to_output.to_stderr end it "renders aliases and old names on separate rows when both exist" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end allow(main_formula).to receive_messages(aliases: ["testball@1.0", "tball"], oldnames: ["foo", "bar"]) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(/^Aliases: testball@1\.0, tball\nOld Names: foo, bar$/).to_stdout .and not_to_output.to_stderr end it "renders only an Old Names row when there are no aliases" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end allow(main_formula).to receive_messages(aliases: [], oldnames: ["foo"]) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(/^Old Names: foo$/).to_stdout .and not_to_output(/^Aliases:/).to_stdout .and not_to_output.to_stderr end it "omits both rows when there are none" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end allow(main_formula).to receive_messages(aliases: [], oldnames: []) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to not_to_output(/^Aliases:/).to_stdout .and not_to_output(/^Old Names:/).to_stdout .and not_to_output.to_stderr end end describe "Installed section" do it "lists this formula alongside installed sibling versioned formulae" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end versioned = formula("testball@0.9") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.9.tar.gz" keg_only :versioned_formula end main_keg = HOMEBREW_CELLAR/"testball/1.0" main_keg.mkpath main_tab = Tab.empty main_tab.tabfile = main_keg/AbstractTab::FILENAME main_tab.write sibling_keg = HOMEBREW_CELLAR/"testball@0.9/0.9" sibling_keg.mkpath sibling_tab = Tab.empty sibling_tab.tabfile = sibling_keg/AbstractTab::FILENAME sibling_tab.write allow(main_formula).to receive(:versioned_formulae).and_return([versioned]) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(Regexp.new( "==> Installed Versions\n" \ ".*testball\\b.*\\s+1\\.0\\s+\\(.*\\)\n" \ ".*testball@0\\.9\\b.*\\s+0\\.9\\s+\\(", )).to_stdout .and not_to_output.to_stderr end it "shows installed → latest only on the newest installed keg of an outdated formula" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-2.0.tar.gz" version "2.0" end ["1.0", "0.9"].each do |version| keg_path = HOMEBREW_CELLAR/"testball/#{version}" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write end allow(main_formula).to receive_messages(versioned_formulae: [], outdated?: true) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(/==> Installed Versions\n.*testball\b.*\s+1\.0 → 2\.0\s+\(/).to_stdout .and not_to_output(/0\.9 →/).to_stdout .and not_to_output.to_stderr end it "hides older non-linked kegs by default" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end ["1.0", "0.9"].each do |version| keg_path = HOMEBREW_CELLAR/"testball/#{version}" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write end allow(main_formula).to receive(:versioned_formulae).and_return([]) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(/==> Installed Versions\n.*testball\b.*\s+1\.0\s+\(/).to_stdout .and not_to_output(/\s+0\.9\s+\(/).to_stdout .and not_to_output.to_stderr end it "marks the currently linked version with `*`" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end versioned = formula("testball@0.9") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.9.tar.gz" keg_only :versioned_formula end main_keg = HOMEBREW_CELLAR/"testball/1.0" main_keg.mkpath main_tab = Tab.empty main_tab.tabfile = main_keg/AbstractTab::FILENAME main_tab.write (HOMEBREW_LINKED_KEGS/"testball").parent.mkpath FileUtils.ln_s(main_keg, HOMEBREW_LINKED_KEGS/"testball") sibling_keg = HOMEBREW_CELLAR/"testball@0.9/0.9" sibling_keg.mkpath sibling_tab = Tab.empty sibling_tab.tabfile = sibling_keg/AbstractTab::FILENAME sibling_tab.write allow(main_formula).to receive_messages(versioned_formulae: [versioned], linked?: true, linked_version: PkgVersion.parse("1.0")) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(/.*testball\b.*\s+1\.0\s+\(.*\)\s+\[Linked\]/).to_stdout .and not_to_output.to_stderr end it "includes the unversioned parent when run on a versioned formula" do info = described_class.new([]) versioned = formula("testball@0.9") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.9.tar.gz" keg_only :versioned_formula end parent = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end versioned_keg = HOMEBREW_CELLAR/"testball@0.9/0.9" versioned_keg.mkpath versioned_tab = Tab.empty versioned_tab.tabfile = versioned_keg/AbstractTab::FILENAME versioned_tab.write parent_keg = HOMEBREW_CELLAR/"testball/1.0" (parent_keg/"bin").mkpath parent_tab = Tab.empty parent_tab.tabfile = parent_keg/AbstractTab::FILENAME parent_tab.write allow(versioned).to receive(:versioned_formulae).and_return([]) allow(Formulary).to receive(:factory).with("testball").and_return(parent) allow(info).to receive(:github_info).with(versioned).and_return("https://example.com/testball.rb") expect { info.info_formula(versioned) } .to output(Regexp.new( "==> Installed Versions\n" \ ".*testball\\b.*\\s+1\\.0\\s+\\(.*\\)\n" \ ".*testball@0\\.9\\b.*\\s+0\\.9\\s+\\(", )).to_stdout .and not_to_output.to_stderr end it "renders the section even when only the current formula is installed" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end keg_path = HOMEBREW_CELLAR/"testball/1.0" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write allow(main_formula).to receive(:versioned_formulae).and_return([]) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(/==> Installed Versions\n.*testball\b.*\s+1\.0\s+\(/).to_stdout .and not_to_output.to_stderr end it "renders the section when the queried formula is uninstalled but a sibling is installed" do info = described_class.new([]) versioned = formula("testball@0.9") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.9.tar.gz" keg_only :versioned_formula end parent = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end parent_keg = HOMEBREW_CELLAR/"testball/1.0" parent_keg.mkpath parent_tab = Tab.empty parent_tab.tabfile = parent_keg/AbstractTab::FILENAME parent_tab.write allow(versioned).to receive(:versioned_formulae).and_return([]) allow(Formulary).to receive(:factory).with("testball").and_return(parent) allow(info).to receive(:github_info).with(versioned).and_return("https://example.com/testball.rb") expect { info.info_formula(versioned) } .to output(/==> Installed Versions\n.*testball\b.*\s+1\.0\s+\(/).to_stdout .and not_to_output(/testball@0\.9 \(0\.9\)/).to_stdout .and not_to_output.to_stderr end it "lists every installed keg of a formula, newest first, with --verbose" do info = described_class.new(["--verbose"]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end ["0.9", "1.0", "0.10"].each do |version| keg_path = HOMEBREW_CELLAR/"testball/#{version}" keg_path.mkpath tab = Tab.empty tab.tabfile = keg_path/AbstractTab::FILENAME tab.write end allow(main_formula).to receive(:versioned_formulae).and_return([]) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to output(Regexp.new( "==> Installed Kegs and Versions\n" \ ".*testball\\b.*\\s+1\\.0\\b.*\\(.*\\)\n" \ "(?: .*\n)*" \ ".*testball\\b.*\\s+0\\.10\\s+\\(.*\\)\n" \ "(?: .*\n)*" \ ".*testball\\b.*\\s+0\\.9\\s+\\(", )).to_stdout .and not_to_output.to_stderr end it "omits the section when nothing in the family is installed" do info = described_class.new([]) main_formula = formula("testball") do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-1.0.tar.gz" end allow(main_formula).to receive(:versioned_formulae).and_return([]) allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb") expect { info.info_formula(main_formula) } .to not_to_output(/==> Installed Versions\b/).to_stdout .and not_to_output.to_stderr end end describe "#github_info" do let(:tap) { CoreTap.instance } it "returns the local path for a formula whose file lives outside its tap" do # Simulates a formula that was removed from its tap but is still installed, # so it gets loaded from the keg's `.brew/` directory by `FromKegLoader`. keg_formula_path = HOMEBREW_CELLAR/"testball/0.1/.brew/testball.rb" formula_instance = formula("testball", path: keg_formula_path, tap:) do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" end expect(described_class.new([]).github_info(formula_instance)) .to eq(keg_formula_path.to_s) end it "returns a GitHub URL for a formula whose file lives inside its tap" do formula_path = tap.new_formula_path("testball") formula_instance = formula("testball", path: formula_path, tap:) do T.bind(self, T.class_of(Formula)) url "https://brew.sh/testball-0.1.tar.gz" end expect(described_class.new([]).github_info(formula_instance)) .to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/" \ "#{formula_path.relative_path_from(tap.path)}") end end end