brew/Library/Homebrew/test/formula_spec.rb

1899 lines
51 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
2017-02-22 00:37:42 +01:00
require "test/support/fixtures/testball"
require "formula"
2017-05-09 23:00:51 +02:00
describe Formula do
alias_matcher :follow_installed_alias, :be_follow_installed_alias
alias_matcher :have_any_version_installed, :be_any_version_installed
alias_matcher :need_migration, :be_migration_needed
2017-02-22 00:37:42 +01:00
2017-05-09 23:00:51 +02:00
alias_matcher :have_changed_installed_alias_target, :be_installed_alias_target_changed
alias_matcher :supersede_an_installed_formula, :be_supersedes_an_installed_formula
alias_matcher :have_changed_alias, :be_alias_changed
2017-02-22 00:37:42 +01:00
2017-05-09 23:00:51 +02:00
alias_matcher :have_option_defined, :be_option_defined
alias_matcher :have_post_install_defined, :be_post_install_defined
2017-05-09 23:00:51 +02:00
alias_matcher :have_test_defined, :be_test_defined
alias_matcher :pour_bottle, :be_pour_bottle
2017-02-22 00:37:42 +01:00
describe "::new" do
let(:klass) do
Class.new(described_class) do
url "https://brew.sh/foo-1.0.tar.gz"
2017-02-22 00:37:42 +01:00
end
end
let(:name) { "formula_name" }
let(:path) { Formulary.core_path(name) }
let(:spec) { :stable }
let(:alias_name) { "baz@1" }
let(:alias_path) { (CoreTap.instance.alias_dir/alias_name).to_s }
2017-02-22 00:37:42 +01:00
let(:f) { klass.new(name, path, spec) }
let(:f_alias) { klass.new(name, path, spec, alias_path: alias_path) }
specify "formula instantiation" do
expect(f.name).to eq(name)
expect(f.specified_name).to eq(name)
expect(f.full_name).to eq(name)
expect(f.full_specified_name).to eq(name)
expect(f.path).to eq(path)
expect(f.alias_path).to be_nil
expect(f.alias_name).to be_nil
expect(f.full_alias_name).to be_nil
expect(f.specified_path).to eq(path)
2017-02-22 00:37:42 +01:00
expect { klass.new }.to raise_error(ArgumentError)
end
specify "formula instantiation with alias" do
expect(f_alias.name).to eq(name)
expect(f_alias.full_name).to eq(name)
expect(f_alias.path).to eq(path)
expect(f_alias.alias_path).to eq(alias_path)
expect(f_alias.alias_name).to eq(alias_name)
expect(f_alias.specified_name).to eq(alias_name)
expect(f_alias.specified_path).to eq(Pathname(alias_path))
2017-02-22 00:37:42 +01:00
expect(f_alias.full_alias_name).to eq(alias_name)
expect(f_alias.full_specified_name).to eq(alias_name)
expect { klass.new }.to raise_error(ArgumentError)
end
context "when in a Tap" do
2017-02-22 00:37:42 +01:00
let(:tap) { Tap.new("foo", "bar") }
let(:path) { (tap.path/"Formula/#{name}.rb") }
let(:full_name) { "#{tap.user}/#{tap.repo}/#{name}" }
let(:full_alias_name) { "#{tap.user}/#{tap.repo}/#{alias_name}" }
specify "formula instantiation" do
expect(f.name).to eq(name)
expect(f.specified_name).to eq(name)
expect(f.full_name).to eq(full_name)
expect(f.full_specified_name).to eq(full_name)
expect(f.path).to eq(path)
expect(f.alias_path).to be_nil
expect(f.alias_name).to be_nil
expect(f.full_alias_name).to be_nil
expect(f.specified_path).to eq(path)
2017-02-22 00:37:42 +01:00
expect { klass.new }.to raise_error(ArgumentError)
end
specify "formula instantiation with alias" do
expect(f_alias.name).to eq(name)
expect(f_alias.full_name).to eq(full_name)
expect(f_alias.path).to eq(path)
expect(f_alias.alias_path).to eq(alias_path)
expect(f_alias.alias_name).to eq(alias_name)
expect(f_alias.specified_name).to eq(alias_name)
expect(f_alias.specified_path).to eq(Pathname(alias_path))
2017-02-22 00:37:42 +01:00
expect(f_alias.full_alias_name).to eq(full_alias_name)
expect(f_alias.full_specified_name).to eq(full_alias_name)
expect { klass.new }.to raise_error(ArgumentError)
end
end
end
describe "#follow_installed_alias?" do
let(:f) do
formula do
url "foo-1.0"
end
end
it "returns true by default" do
expect(f).to follow_installed_alias
end
it "can be set to true" do
f.follow_installed_alias = true
expect(f).to follow_installed_alias
end
it "can be set to false" do
f.follow_installed_alias = false
expect(f).not_to follow_installed_alias
end
end
describe "#versioned_formula?" do
let(:f) do
formula "foo" do
url "foo-1.0"
end
end
let(:f2) do
formula "foo@2.0" do
url "foo-2.0"
end
end
it "returns true for @-versioned formulae" do
expect(f2.versioned_formula?).to be true
end
it "returns false for non-@-versioned formulae" do
expect(f.versioned_formula?).to be false
end
end
describe "#versioned_formulae" do
let(:f) do
formula "foo" do
url "foo-1.0"
end
end
let(:f2) do
formula "foo@2.0" do
url "foo-2.0"
end
end
before do
2022-11-09 14:45:43 +00:00
# don't try to load/fetch gcc/glibc
allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false)
2022-11-09 14:45:43 +00:00
allow(Formulary).to receive(:load_formula_from_path).with(f2.name, f2.path).and_return(f2)
allow(Formulary).to receive(:factory).with(f2.name).and_return(f2)
allow(f).to receive(:versioned_formulae_names).and_return([f2.name])
end
it "returns array with versioned formulae" do
FileUtils.touch f.path
FileUtils.touch f2.path
expect(f.versioned_formulae).to eq [f2]
end
it "returns empty array for non-@-versioned formulae" do
FileUtils.touch f.path
FileUtils.touch f2.path
expect(f2.versioned_formulae).to be_empty
end
end
2017-02-22 00:37:42 +01:00
example "installed alias with core" do
f = formula do
url "foo-1.0"
end
build_values_with_no_installed_alias = [
BuildOptions.new(Options.new, f.options),
2017-02-22 00:37:42 +01:00
Tab.new(source: { "path" => f.path.to_s }),
]
build_values_with_no_installed_alias.each do |build|
f.build = build
expect(f.installed_alias_path).to be_nil
expect(f.installed_alias_name).to be_nil
expect(f.full_installed_alias_name).to be_nil
2017-02-22 00:37:42 +01:00
expect(f.installed_specified_name).to eq(f.name)
expect(f.full_installed_specified_name).to eq(f.name)
end
alias_name = "bar"
alias_path = "#{CoreTap.instance.alias_dir}/#{alias_name}"
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf f.path, alias_path
2017-02-22 00:37:42 +01:00
f.build = Tab.new(source: { "path" => alias_path })
expect(f.installed_alias_path).to eq(alias_path)
expect(f.installed_alias_name).to eq(alias_name)
expect(f.full_installed_alias_name).to eq(alias_name)
expect(f.installed_specified_name).to eq(alias_name)
expect(f.full_installed_specified_name).to eq(alias_name)
end
example "installed alias with tap" do
tap = Tap.new("user", "repo")
name = "foo"
path = tap.path/"Formula/#{name}.rb"
2017-02-22 00:37:42 +01:00
f = formula name, path: path do
url "foo-1.0"
end
build_values_with_no_installed_alias = [
BuildOptions.new(Options.new, f.options),
Tab.new(source: { "path" => f.path.to_s }),
]
2017-02-22 00:37:42 +01:00
build_values_with_no_installed_alias.each do |build|
f.build = build
expect(f.installed_alias_path).to be_nil
expect(f.installed_alias_name).to be_nil
expect(f.full_installed_alias_name).to be_nil
2017-02-22 00:37:42 +01:00
expect(f.installed_specified_name).to eq(f.name)
expect(f.full_installed_specified_name).to eq(f.full_name)
end
alias_name = "bar"
full_alias_name = "#{tap.user}/#{tap.repo}/#{alias_name}"
alias_path = "#{tap.alias_dir}/#{alias_name}"
tap.alias_dir.mkpath
FileUtils.ln_sf f.path, alias_path
2017-02-22 00:37:42 +01:00
f.build = Tab.new(source: { "path" => alias_path })
expect(f.installed_alias_path).to eq(alias_path)
expect(f.installed_alias_name).to eq(alias_name)
expect(f.full_installed_alias_name).to eq(full_alias_name)
expect(f.installed_specified_name).to eq(alias_name)
expect(f.full_installed_specified_name).to eq(full_alias_name)
FileUtils.rm_rf HOMEBREW_LIBRARY/"Taps/user"
2017-02-22 00:37:42 +01:00
end
specify "#prefix" do
f = Testball.new
expect(f.prefix).to eq(HOMEBREW_CELLAR/f.name/"0.1")
2022-09-13 09:43:09 +01:00
expect(f.prefix).to be_a(Pathname)
2017-02-22 00:37:42 +01:00
end
example "revised prefix" do
f = Class.new(Testball) { revision(1) }.new
expect(f.prefix).to eq(HOMEBREW_CELLAR/f.name/"0.1_1")
end
specify "#any_version_installed?" do
f = formula do
url "foo"
version "1.0"
end
expect(f).not_to have_any_version_installed
prefix = HOMEBREW_CELLAR/f.name/"0.1"
prefix.mkpath
FileUtils.touch prefix/Tab::FILENAME
expect(f).to have_any_version_installed
end
specify "#migration_needed" do
f = Testball.new("newname")
2023-04-27 04:09:28 +01:00
f.instance_variable_set(:@oldnames, ["oldname"])
2017-02-22 00:37:42 +01:00
f.instance_variable_set(:@tap, CoreTap.instance)
oldname_prefix = (HOMEBREW_CELLAR/"oldname/2.20")
newname_prefix = (HOMEBREW_CELLAR/"newname/2.10")
oldname_prefix.mkpath
oldname_tab = Tab.empty
oldname_tab.tabfile = oldname_prefix/Tab::FILENAME
oldname_tab.write
expect(f).not_to need_migration
oldname_tab.tabfile.unlink
oldname_tab.source["tap"] = "homebrew/core"
oldname_tab.write
expect(f).to need_migration
newname_prefix.mkpath
expect(f).not_to need_migration
end
describe "#latest_version_installed?" do
2017-02-22 00:37:42 +01:00
let(:f) { Testball.new }
it "returns false if the #latest_installed_prefix is not a directory" do
2023-01-22 17:07:24 -08:00
allow(f).to receive(:latest_installed_prefix).and_return(instance_double(Pathname, directory?: false))
expect(f).not_to be_latest_version_installed
2017-02-22 00:37:42 +01:00
end
it "returns false if the #latest_installed_prefix does not have children" do
2023-01-22 17:07:24 -08:00
allow(f).to receive(:latest_installed_prefix)
.and_return(instance_double(Pathname, directory?: true, children: []))
expect(f).not_to be_latest_version_installed
2017-02-22 00:37:42 +01:00
end
it "returns true if the #latest_installed_prefix has children" do
2023-01-22 17:07:24 -08:00
allow(f).to receive(:latest_installed_prefix)
.and_return(instance_double(Pathname, directory?: true, children: [double]))
expect(f).to be_latest_version_installed
2017-02-22 00:37:42 +01:00
end
end
describe "#latest_installed_prefix" do
2017-02-22 00:37:42 +01:00
let(:f) do
formula do
url "foo"
version "1.9"
head "foo"
end
end
let(:stable_prefix) { HOMEBREW_CELLAR/f.name/f.version }
let(:head_prefix) { HOMEBREW_CELLAR/f.name/f.head.version }
it "is the same as #prefix by default" do
expect(f.latest_installed_prefix).to eq(f.prefix)
2017-02-22 00:37:42 +01:00
end
it "returns the stable prefix if it is installed" do
stable_prefix.mkpath
expect(f.latest_installed_prefix).to eq(stable_prefix)
2017-02-22 00:37:42 +01:00
end
it "returns the head prefix if it is installed" do
head_prefix.mkpath
expect(f.latest_installed_prefix).to eq(head_prefix)
2017-02-22 00:37:42 +01:00
end
it "returns the stable prefix if head is outdated" do
head_prefix.mkpath
tab = Tab.empty
tab.tabfile = head_prefix/Tab::FILENAME
tab.source["versions"] = { "stable" => "1.0" }
tab.write
expect(f.latest_installed_prefix).to eq(stable_prefix)
2017-02-22 00:37:42 +01:00
end
it "returns the head prefix if the active specification is :head" do
f.active_spec = :head
expect(f.latest_installed_prefix).to eq(head_prefix)
2017-02-22 00:37:42 +01:00
end
end
describe "#latest_head_prefix" do
let(:f) { Testball.new }
it "returns the latest head prefix" do
stamps_with_revisions = [
[111111, 1],
[222222, 0],
[222222, 1],
[222222, 2],
]
stamps_with_revisions.each do |stamp, revision|
version = "HEAD-#{stamp}"
2019-04-20 14:07:29 +09:00
version = "#{version}_#{revision}" unless revision.zero?
2017-02-22 00:37:42 +01:00
prefix = f.rack/version
prefix.mkpath
tab = Tab.empty
tab.tabfile = prefix/Tab::FILENAME
tab.source_modified_time = stamp
tab.write
end
prefix = HOMEBREW_CELLAR/f.name/"HEAD-222222_2"
expect(f.latest_head_prefix).to eq(prefix)
end
end
specify "equality" do
x = Testball.new
y = Testball.new
expect(x).to eq(y)
expect(x).to eql(y)
expect(x.hash).to eq(y.hash)
end
specify "inequality" do
x = Testball.new("foo")
y = Testball.new("bar")
expect(x).not_to eq(y)
expect(x).not_to eql(y)
expect(x.hash).not_to eq(y.hash)
end
specify "comparison with non formula objects does not raise" do
expect(Object.new).not_to eq(Testball.new)
end
specify "#<=>" do
expect(Testball.new <=> Object.new).to be_nil
2017-02-22 00:37:42 +01:00
end
describe "#installed_alias_path" do
example "alias paths with build options" do
alias_path = (CoreTap.instance.alias_dir/"another_name")
f = formula alias_path: alias_path do
url "foo-1.0"
end
f.build = BuildOptions.new(Options.new, f.options)
2017-02-22 00:37:42 +01:00
expect(f.alias_path).to eq(alias_path)
expect(f.installed_alias_path).to be_nil
2017-02-22 00:37:42 +01:00
end
example "alias paths with tab with non alias source path" do
alias_path = (CoreTap.instance.alias_dir/"another_name")
source_path = CoreTap.instance.new_formula_path("another_other_name")
2017-02-22 00:37:42 +01:00
f = formula alias_path: alias_path do
url "foo-1.0"
end
f.build = Tab.new(source: { "path" => source_path.to_s })
expect(f.alias_path).to eq(alias_path)
expect(f.installed_alias_path).to be_nil
2017-02-22 00:37:42 +01:00
end
example "alias paths with tab with alias source path" do
alias_path = (CoreTap.instance.alias_dir/"another_name")
source_path = (CoreTap.instance.alias_dir/"another_other_name")
f = formula alias_path: alias_path do
url "foo-1.0"
end
f.build = Tab.new(source: { "path" => source_path.to_s })
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf f.path, source_path
2017-02-22 00:37:42 +01:00
expect(f.alias_path).to eq(alias_path)
expect(f.installed_alias_path).to eq(source_path.to_s)
end
end
describe "::inreplace" do
specify "raises build error on failure" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
end
expect { f.inreplace([]) }.to raise_error(BuildError)
2020-11-01 01:57:02 -04:00
end
specify "replaces text in file" do
file = Tempfile.new("test")
File.binwrite(file, <<~EOS)
ab
bc
cd
EOS
f = formula do
url "https://brew.sh/test-1.0.tbz"
end
f.inreplace(file.path) do |s|
s.gsub!("bc", "yz")
end
expect(File.binread(file)).to eq <<~EOS
ab
yz
cd
EOS
end
2020-11-01 01:57:02 -04:00
end
2017-02-22 00:37:42 +01:00
describe "::installed_with_alias_path" do
specify "with alias path with nil" do
expect(described_class.installed_with_alias_path(nil)).to be_empty
end
specify "with alias path with a path" do
alias_path = "#{CoreTap.instance.alias_dir}/alias"
different_alias_path = "#{CoreTap.instance.alias_dir}/another_alias"
formula_with_alias = formula "foo" do
url "foo-1.0"
end
formula_with_alias.build = Tab.empty
formula_with_alias.build.source["path"] = alias_path
formula_without_alias = formula "bar" do
url "bar-1.0"
end
formula_without_alias.build = Tab.empty
formula_without_alias.build.source["path"] = formula_without_alias.path.to_s
formula_with_different_alias = formula "baz" do
url "baz-1.0"
end
formula_with_different_alias.build = Tab.empty
formula_with_different_alias.build.source["path"] = different_alias_path
formulae = [
formula_with_alias,
formula_without_alias,
formula_with_different_alias,
]
allow(described_class).to receive(:installed).and_return(formulae)
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf formula_with_alias.path, alias_path
2017-02-22 00:37:42 +01:00
expect(described_class.installed_with_alias_path(alias_path))
.to eq([formula_with_alias])
end
end
specify "spec integration" do
f = formula do
homepage "https://brew.sh"
2017-02-22 00:37:42 +01:00
url "https://brew.sh/test-0.1.tbz"
2018-08-06 16:23:07 +02:00
mirror "https://example.org/test-0.1.tbz"
2017-02-22 00:37:42 +01:00
sha256 TEST_SHA256
head "https://brew.sh/test.git", tag: "foo"
2017-02-22 00:37:42 +01:00
end
expect(f.homepage).to eq("https://brew.sh")
expect(f.version).to eq(Version.new("0.1"))
2017-02-22 00:37:42 +01:00
expect(f).to be_stable
expect(f.stable.version).to eq(Version.new("0.1"))
expect(f.head.version).to eq(Version.new("HEAD"))
2017-02-22 00:37:42 +01:00
end
specify "#active_spec=" do
f = formula do
url "foo"
version "1.0"
revision 1
end
expect(f.active_spec_sym).to eq(:stable)
expect(f.send(:active_spec)).to eq(f.stable)
expect(f.pkg_version.to_s).to eq("1.0_1")
expect { f.active_spec = :head }.to raise_error(FormulaSpecificationError)
end
specify "class specs are always initialized" do
f = formula do
url "foo-1.0"
end
2022-09-13 09:43:09 +01:00
expect(f.class.stable).to be_a(SoftwareSpec)
expect(f.class.head).to be_a(SoftwareSpec)
2017-02-22 00:37:42 +01:00
end
specify "instance specs have different references" do
f = Testball.new
f2 = Testball.new
expect(f.stable.owner).to equal(f)
expect(f2.stable.owner).to equal(f2)
end
2017-02-22 00:37:42 +01:00
specify "incomplete instance specs are not accessible" do
f = formula do
url "foo-1.0"
end
expect(f.head).to be_nil
2017-02-22 00:37:42 +01:00
end
it "honors attributes declared before specs" do
f = formula do
url "foo-1.0"
depends_on "foo"
end
expect(f.class.stable.deps.first.name).to eq("foo")
expect(f.class.head.deps.first.name).to eq("foo")
end
describe "#pkg_version" do
specify "simple version" do
f = formula do
url "foo-1.0.bar"
end
expect(f.pkg_version).to eq(PkgVersion.parse("1.0"))
end
specify "version with revision" do
f = formula do
url "foo-1.0.bar"
revision 1
end
expect(f.pkg_version).to eq(PkgVersion.parse("1.0_1"))
end
specify "head uses revisions" do
f = formula "test", spec: :head do
url "foo-1.0.bar"
revision 1
head "foo"
end
expect(f.pkg_version).to eq(PkgVersion.parse("HEAD_1"))
end
end
specify "#update_head_version" do
f = formula do
head "foo", using: :git
end
cached_location = f.head.downloader.cached_location
cached_location.mkpath
cached_location.cd do
FileUtils.touch "LICENSE"
2017-07-29 19:55:05 +02:00
system("git", "init")
system("git", "add", "--all")
system("git", "commit", "-m", "Initial commit")
2017-02-22 00:37:42 +01:00
end
f.update_head_version
expect(f.head.version).to eq(Version.new("HEAD-5658946"))
2017-02-22 00:37:42 +01:00
end
specify "#desc" do
f = formula do
desc "a formula"
url "foo-1.0"
end
expect(f.desc).to eq("a formula")
end
specify "#post_install_defined?" do
f1 = formula do
url "foo-1.0"
def post_install
# do nothing
end
end
f2 = formula do
url "foo-1.0"
end
expect(f1).to have_post_install_defined
expect(f2).not_to have_post_install_defined
end
2017-02-22 00:37:42 +01:00
specify "test fixtures" do
f1 = formula do
url "foo-1.0"
end
expect(f1.test_fixtures("foo")).to eq(Pathname.new("#{HOMEBREW_LIBRARY_PATH}/test/support/fixtures/foo"))
end
2020-03-16 01:37:49 +05:30
specify "#livecheck" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
livecheck do
skip "foo"
url "https://brew.sh/test/releases"
2020-06-01 11:37:58 +05:30
regex(/test-v?(\d+(?:\.\d+)+)\.t/i)
2020-03-16 01:37:49 +05:30
end
end
expect(f.livecheck.skip?).to be true
expect(f.livecheck.skip_msg).to eq("foo")
expect(f.livecheck.url).to eq("https://brew.sh/test/releases")
2020-06-01 11:37:58 +05:30
expect(f.livecheck.regex).to eq(/test-v?(\d+(?:\.\d+)+)\.t/i)
2020-03-16 01:37:49 +05:30
end
describe "#livecheckable?" do
specify "no livecheck block defined" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
end
expect(f.livecheckable?).to be false
end
specify "livecheck block defined" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
livecheck do
2020-06-01 11:37:58 +05:30
regex(/test-v?(\d+(?:\.\d+)+)\.t/i)
2020-03-16 01:37:49 +05:30
end
end
expect(f.livecheckable?).to be true
end
2020-05-31 00:10:46 +05:30
specify "livecheck references Formula URL" do
f = formula do
homepage "https://brew.sh/test"
url "https://brew.sh/test-1.0.tbz"
livecheck do
url :homepage
2020-06-01 11:37:58 +05:30
regex(/test-v?(\d+(?:\.\d+)+)\.t/i)
2020-05-31 00:10:46 +05:30
end
end
expect(f.livecheck.url).to eq(:homepage)
2020-05-31 00:10:46 +05:30
end
2020-03-16 01:37:49 +05:30
end
describe "#service" do
specify "no service defined" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
end
service: add custom service name DSL The main thing is that this DSL allows us to provide an interface that can be serialized to the JSON API. Changes: - Homebrew::Service - Adds `#service_name` and `#plist_name` methods - Each is now included in the `#serialize` method as well - Eval block on instantiation - Before we lazy evaluated this but the cost is not significant and it complicated the code a bunch. This only gets called during install, when evaluating caveats and in the `brew service` command. It skips this evaluation if the service block isn't there. - Add `#command?` helper to avoid `#command.blank?` and `#command.present?` - Formula - `#service` now returns a service whenever it's called. This call is hidden behind a call to `#service?` most of the time anyway so this should be fine. - `#plist_name` and `#service_name` now call the methods of the same name on the service class. This should have already been in the service object to begin with and keeping these methods here helps preserve backwards compatibility with people who were overwriting these methods before. - Caveats - Prefer `service#command?` - Add helpers for checking on service commands - This duplicates some of the work in `brew services`. Maybe we should merge that repo in at some point. - Check for installed service at `#plist_name` or `#service_name`. I think this should be used instead of `Keg#plist_installed?` which checked for any plist file. We should think about deprecating `#plist_installed?` in the future. - Stop using `ps aux | grep #{formula.plist_name}` to check for service files because it was inaccurate (it always returns true on my machine) because the grep process is started before the ps process. - Note: The behavior is the same as it was before. This means that caveats only show up for custom service files on install or if they're already installed. Otherwise it won't show up in `brew info`. This is because it has to check first if the service file has been installed. - Utils::Service - Add utils for evaluating if a service is installed and running. This duplicates some of the work already found in `brew services`. We should seriously consider merging `brew services` with the main brew repo in the future since it's already tightly coupled to the code in the main repo. - Formulary.load_formula_from_api - Be more explicit about which types can be deserialized into run params since it is now possible for run params to be nil. - Update and add tests
2023-04-13 23:33:31 -07:00
expect(f.service.serialize).to eq({})
2020-12-11 23:14:50 +01:00
end
specify "service complicated" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
service do
run [opt_bin/"beanstalkd"]
run_type :immediate
error_log_path var/"log/beanstalkd.error.log"
log_path var/"log/beanstalkd.log"
working_dir var
keep_alive true
end
end
service: add custom service name DSL The main thing is that this DSL allows us to provide an interface that can be serialized to the JSON API. Changes: - Homebrew::Service - Adds `#service_name` and `#plist_name` methods - Each is now included in the `#serialize` method as well - Eval block on instantiation - Before we lazy evaluated this but the cost is not significant and it complicated the code a bunch. This only gets called during install, when evaluating caveats and in the `brew service` command. It skips this evaluation if the service block isn't there. - Add `#command?` helper to avoid `#command.blank?` and `#command.present?` - Formula - `#service` now returns a service whenever it's called. This call is hidden behind a call to `#service?` most of the time anyway so this should be fine. - `#plist_name` and `#service_name` now call the methods of the same name on the service class. This should have already been in the service object to begin with and keeping these methods here helps preserve backwards compatibility with people who were overwriting these methods before. - Caveats - Prefer `service#command?` - Add helpers for checking on service commands - This duplicates some of the work in `brew services`. Maybe we should merge that repo in at some point. - Check for installed service at `#plist_name` or `#service_name`. I think this should be used instead of `Keg#plist_installed?` which checked for any plist file. We should think about deprecating `#plist_installed?` in the future. - Stop using `ps aux | grep #{formula.plist_name}` to check for service files because it was inaccurate (it always returns true on my machine) because the grep process is started before the ps process. - Note: The behavior is the same as it was before. This means that caveats only show up for custom service files on install or if they're already installed. Otherwise it won't show up in `brew info`. This is because it has to check first if the service file has been installed. - Utils::Service - Add utils for evaluating if a service is installed and running. This duplicates some of the work already found in `brew services`. We should seriously consider merging `brew services` with the main brew repo in the future since it's already tightly coupled to the code in the main repo. - Formulary.load_formula_from_api - Be more explicit about which types can be deserialized into run params since it is now possible for run params to be nil. - Update and add tests
2023-04-13 23:33:31 -07:00
expect(f.service.serialize.keys)
.to contain_exactly(:run, :run_type, :error_log_path, :log_path, :working_dir, :keep_alive)
end
2020-12-11 23:14:50 +01:00
specify "service uses simple run" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
service do
run opt_bin/"beanstalkd"
end
2020-12-11 23:14:50 +01:00
end
service: add custom service name DSL The main thing is that this DSL allows us to provide an interface that can be serialized to the JSON API. Changes: - Homebrew::Service - Adds `#service_name` and `#plist_name` methods - Each is now included in the `#serialize` method as well - Eval block on instantiation - Before we lazy evaluated this but the cost is not significant and it complicated the code a bunch. This only gets called during install, when evaluating caveats and in the `brew service` command. It skips this evaluation if the service block isn't there. - Add `#command?` helper to avoid `#command.blank?` and `#command.present?` - Formula - `#service` now returns a service whenever it's called. This call is hidden behind a call to `#service?` most of the time anyway so this should be fine. - `#plist_name` and `#service_name` now call the methods of the same name on the service class. This should have already been in the service object to begin with and keeping these methods here helps preserve backwards compatibility with people who were overwriting these methods before. - Caveats - Prefer `service#command?` - Add helpers for checking on service commands - This duplicates some of the work in `brew services`. Maybe we should merge that repo in at some point. - Check for installed service at `#plist_name` or `#service_name`. I think this should be used instead of `Keg#plist_installed?` which checked for any plist file. We should think about deprecating `#plist_installed?` in the future. - Stop using `ps aux | grep #{formula.plist_name}` to check for service files because it was inaccurate (it always returns true on my machine) because the grep process is started before the ps process. - Note: The behavior is the same as it was before. This means that caveats only show up for custom service files on install or if they're already installed. Otherwise it won't show up in `brew info`. This is because it has to check first if the service file has been installed. - Utils::Service - Add utils for evaluating if a service is installed and running. This duplicates some of the work already found in `brew services`. We should seriously consider merging `brew services` with the main brew repo in the future since it's already tightly coupled to the code in the main repo. - Formulary.load_formula_from_api - Be more explicit about which types can be deserialized into run params since it is now possible for run params to be nil. - Update and add tests
2023-04-13 23:33:31 -07:00
expect(f.service.serialize.keys).to contain_exactly(:run, :run_type)
end
specify "service with only custom names" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
service do
name macos: "custom.macos.beanstalkd", linux: "custom.linux.beanstalkd"
service: add custom service name DSL The main thing is that this DSL allows us to provide an interface that can be serialized to the JSON API. Changes: - Homebrew::Service - Adds `#service_name` and `#plist_name` methods - Each is now included in the `#serialize` method as well - Eval block on instantiation - Before we lazy evaluated this but the cost is not significant and it complicated the code a bunch. This only gets called during install, when evaluating caveats and in the `brew service` command. It skips this evaluation if the service block isn't there. - Add `#command?` helper to avoid `#command.blank?` and `#command.present?` - Formula - `#service` now returns a service whenever it's called. This call is hidden behind a call to `#service?` most of the time anyway so this should be fine. - `#plist_name` and `#service_name` now call the methods of the same name on the service class. This should have already been in the service object to begin with and keeping these methods here helps preserve backwards compatibility with people who were overwriting these methods before. - Caveats - Prefer `service#command?` - Add helpers for checking on service commands - This duplicates some of the work in `brew services`. Maybe we should merge that repo in at some point. - Check for installed service at `#plist_name` or `#service_name`. I think this should be used instead of `Keg#plist_installed?` which checked for any plist file. We should think about deprecating `#plist_installed?` in the future. - Stop using `ps aux | grep #{formula.plist_name}` to check for service files because it was inaccurate (it always returns true on my machine) because the grep process is started before the ps process. - Note: The behavior is the same as it was before. This means that caveats only show up for custom service files on install or if they're already installed. Otherwise it won't show up in `brew info`. This is because it has to check first if the service file has been installed. - Utils::Service - Add utils for evaluating if a service is installed and running. This duplicates some of the work already found in `brew services`. We should seriously consider merging `brew services` with the main brew repo in the future since it's already tightly coupled to the code in the main repo. - Formulary.load_formula_from_api - Be more explicit about which types can be deserialized into run params since it is now possible for run params to be nil. - Update and add tests
2023-04-13 23:33:31 -07:00
end
end
expect(f.plist_name).to eq("custom.macos.beanstalkd")
expect(f.service_name).to eq("custom.linux.beanstalkd")
expect(f.service.serialize.keys).to contain_exactly(:name)
2020-12-11 23:14:50 +01:00
end
specify "service helpers return data" do
f = formula do
url "https://brew.sh/test-1.0.tbz"
end
expect(f.plist_name).to eq("homebrew.mxcl.formula_name")
expect(f.service_name).to eq("homebrew.formula_name")
expect(f.launchd_service_path).to eq(HOMEBREW_PREFIX/"opt/formula_name/homebrew.mxcl.formula_name.plist")
expect(f.systemd_service_path).to eq(HOMEBREW_PREFIX/"opt/formula_name/homebrew.formula_name.service")
2021-11-20 15:14:50 +01:00
expect(f.systemd_timer_path).to eq(HOMEBREW_PREFIX/"opt/formula_name/homebrew.formula_name.timer")
end
2020-12-11 23:14:50 +01:00
end
2017-02-22 00:37:42 +01:00
specify "dependencies" do
2022-11-09 14:45:43 +00:00
# don't try to load/fetch gcc/glibc
allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false)
2022-11-09 14:45:43 +00:00
2017-02-22 00:37:42 +01:00
f1 = formula "f1" do
url "f1-1.0"
end
f2 = formula "f2" do
url "f2-1.0"
end
f3 = formula "f3" do
url "f3-1.0"
depends_on "f1" => :build
depends_on "f2"
end
f4 = formula "f4" do
url "f4-1.0"
depends_on "f1"
end
stub_formula_loader(f1)
stub_formula_loader(f2)
stub_formula_loader(f3)
stub_formula_loader(f4)
f5 = formula "f5" do
url "f5-1.0"
depends_on "f3" => :build
depends_on "f4"
end
expect(f5.deps.map(&:name)).to eq(["f3", "f4"])
2017-05-29 18:24:52 +01:00
expect(f5.recursive_dependencies.map(&:name)).to eq(%w[f1 f2 f3 f4])
2017-02-22 00:37:42 +01:00
expect(f5.runtime_dependencies.map(&:name)).to eq(["f1", "f4"])
end
describe "#runtime_dependencies" do
specify "runtime dependencies with optional deps from tap" do
tap_loader = double
2017-02-22 00:37:42 +01:00
2022-11-09 14:45:43 +00:00
# don't try to load/fetch gcc/glibc
allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false)
2022-11-09 14:45:43 +00:00
allow(tap_loader).to receive(:get_formula).and_raise(RuntimeError, "tried resolving tap formula")
allow(Formulary).to receive(:loader_for).with("foo/bar/f1", from: nil).and_return(tap_loader)
f2_path = Tap.new("baz", "qux").path/"Formula/f2.rb"
stub_formula_loader(formula("f2", path: f2_path) { url("f2-1.0") }, "baz/qux/f2")
2017-02-22 00:37:42 +01:00
f3 = formula "f3" do
url "f3-1.0"
depends_on "foo/bar/f1" => :optional
depends_on "baz/qux/f2"
end
expect(f3.runtime_dependencies.map(&:name)).to eq(["baz/qux/f2"])
f1_path = Tap.new("foo", "bar").path/"Formula/f1.rb"
stub_formula_loader(formula("f1", path: f1_path) { url("f1-1.0") }, "foo/bar/f1")
f3.build = BuildOptions.new(Options.create(["--with-f1"]), f3.options)
expect(f3.runtime_dependencies.map(&:name)).to eq(["foo/bar/f1", "baz/qux/f2"])
2017-02-22 00:37:42 +01:00
end
it "includes non-declared direct dependencies" do
formula = Class.new(Testball).new
dependency = formula("dependency") { url "f-1.0" }
2017-02-22 00:37:42 +01:00
formula.brew { formula.install }
keg = Keg.for(formula.latest_installed_prefix)
keg.link
2017-02-22 00:37:42 +01:00
2023-01-22 17:07:24 -08:00
linkage_checker = instance_double(LinkageChecker, "linkage checker", undeclared_deps: [dependency.name])
allow(LinkageChecker).to receive(:new).and_return(linkage_checker)
expect(formula.runtime_dependencies.map(&:name)).to eq [dependency.name]
end
it "handles bad tab runtime_dependencies" do
formula = Class.new(Testball).new
formula.brew { formula.install }
tab = Tab.create(formula, DevelopmentTools.default_compiler, :libcxx)
tab.runtime_dependencies = ["foo"]
tab.write
keg = Keg.for(formula.latest_installed_prefix)
keg.link
expect(formula.runtime_dependencies.map(&:name)).to be_empty
end
2017-02-22 00:37:42 +01:00
end
specify "requirements" do
2022-11-09 14:45:43 +00:00
# don't try to load/fetch gcc/glibc
allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false)
2022-11-09 14:45:43 +00:00
2017-02-22 00:37:42 +01:00
f1 = formula "f1" do
url "f1-1"
depends_on xcode: ["1.0", :optional]
end
stub_formula_loader(f1)
xcode = XcodeRequirement.new(["1.0", :optional])
expect(Set.new(f1.recursive_requirements)).to eq(Set[])
2017-02-22 00:37:42 +01:00
f1.build = BuildOptions.new(Options.create(["--with-xcode"]), f1.options)
2017-02-22 00:37:42 +01:00
expect(Set.new(f1.recursive_requirements)).to eq(Set[xcode])
2017-02-22 00:37:42 +01:00
f1.build = f1.stable.build
f2 = formula "f2" do
url "f2-1"
depends_on "f1"
end
expect(Set.new(f2.recursive_requirements)).to eq(Set[])
2023-01-26 21:18:24 -08:00
expect(
f2.recursive_requirements do
2023-01-26 21:18:24 -08:00
# do nothing
end.to_set,
2023-01-26 21:18:24 -08:00
).to eq(Set[xcode])
2017-02-22 00:37:42 +01:00
requirements = f2.recursive_requirements do |_dependent, requirement|
Requirement.prune if requirement.is_a?(XcodeRequirement)
2017-02-22 00:37:42 +01:00
end
expect(Set.new(requirements)).to eq(Set[])
2017-02-22 00:37:42 +01:00
end
specify "#to_hash" do
f1 = formula "foo" do
url "foo-1.0"
bottle do
sha256 cellar: :any, Utils::Bottles.tag.to_sym => TEST_SHA256
2017-02-22 00:37:42 +01:00
end
end
h = f1.to_hash
expect(h).to be_a(Hash)
expect(h["name"]).to eq("foo")
expect(h["full_name"]).to eq("foo")
expect(h["tap"]).to eq("homebrew/core")
2017-02-22 00:37:42 +01:00
expect(h["versions"]["stable"]).to eq("1.0")
expect(h["versions"]["bottle"]).to be_truthy
end
describe "#to_hash_with_variations", :needs_macos do
let(:formula_path) { CoreTap.new.new_formula_path("foo-variations") }
2022-07-21 16:41:23 +02:00
let(:formula_content) do
<<~RUBY
class FooVariations < Formula
url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
sha256 TESTBALL_SHA256
on_intel do
depends_on "intel-formula"
end
on_big_sur do
depends_on "big-sur-formula"
end
on_catalina :or_older do
depends_on "catalina-or-older-formula"
end
on_linux do
depends_on "linux-formula"
end
end
RUBY
end
let(:expected_variations) do
2022-07-21 16:41:23 +02:00
<<~JSON
{
"monterey": {
"dependencies": [
"intel-formula"
]
},
"big_sur": {
"dependencies": [
"intel-formula",
"big-sur-formula"
]
},
2023-05-13 22:35:08 +02:00
"arm64_big_sur": {
"dependencies": [
"big-sur-formula"
]
},
2022-07-21 16:41:23 +02:00
"catalina": {
"dependencies": [
"intel-formula",
"catalina-or-older-formula"
]
},
"mojave": {
"dependencies": [
"intel-formula",
"catalina-or-older-formula"
]
},
"x86_64_linux": {
"dependencies": [
"intel-formula",
"linux-formula"
]
}
}
JSON
end
2022-07-21 16:41:23 +02:00
before do
# Use a more limited os list to shorten the variations hash
os_list = [:monterey, :big_sur, :catalina, :mojave, :linux]
stub_const("OnSystem::ALL_OS_ARCH_COMBINATIONS", os_list.product(OnSystem::ARCH_OPTIONS))
2022-07-21 16:41:23 +02:00
# For consistency, always run on Monterey and ARM
allow(MacOS).to receive(:version).and_return(MacOSVersion.new("12"))
2022-07-21 16:41:23 +02:00
allow(Hardware::CPU).to receive(:type).and_return(:arm)
formula_path.dirname.mkpath
formula_path.write formula_content
end
it "returns the correct variations hash" do
h = Formulary.factory("foo-variations").to_hash_with_variations
expect(h).to be_a(Hash)
expect(JSON.pretty_generate(h["variations"])).to eq expected_variations.strip
end
end
2017-02-22 00:37:42 +01:00
describe "#eligible_kegs_for_cleanup" do
it "returns Kegs eligible for cleanup" do
f1 = Class.new(Testball) do
version("1.0")
end.new
f2 = Class.new(Testball) do
version("0.2")
version_scheme(1)
end.new
f3 = Class.new(Testball) do
version("0.3")
version_scheme(1)
end.new
f4 = Class.new(Testball) do
version("0.1")
version_scheme(2)
end.new
2017-07-29 19:55:05 +02:00
[f1, f2, f3, f4].each do |f|
f.brew { f.install }
Tab.create(f, DevelopmentTools.default_compiler, :libcxx).write
2017-02-22 00:37:42 +01:00
end
expect(f1).to be_latest_version_installed
expect(f2).to be_latest_version_installed
expect(f3).to be_latest_version_installed
expect(f4).to be_latest_version_installed
2017-02-22 00:37:42 +01:00
expect(f3.eligible_kegs_for_cleanup.sort_by(&:version))
.to eq([f2, f1].map { |f| Keg.new(f.prefix) })
end
specify "with pinned Keg" do
f1 = Class.new(Testball) { version("0.1") }.new
f2 = Class.new(Testball) { version("0.2") }.new
f3 = Class.new(Testball) { version("0.3") }.new
2017-07-29 19:55:05 +02:00
f1.brew { f1.install }
f1.pin
f2.brew { f2.install }
f3.brew { f3.install }
2017-02-22 00:37:42 +01:00
expect(f1.prefix).to eq((HOMEBREW_PINNED_KEGS/f1.name).resolved_path)
expect(f1).to be_latest_version_installed
expect(f2).to be_latest_version_installed
expect(f3).to be_latest_version_installed
2017-07-29 19:55:05 +02:00
expect(f3.eligible_kegs_for_cleanup).to eq([Keg.new(f2.prefix)])
2017-02-22 00:37:42 +01:00
end
specify "with HEAD installed" do
f = formula do
version("0.1")
head("foo")
end
2021-05-28 11:24:51 -04:00
["0.0.1", "0.0.2", "0.1", "HEAD-000000", "HEAD-111111", "HEAD-111111_1"].each do |version|
prefix = f.prefix(version)
2017-02-22 00:37:42 +01:00
prefix.mkpath
tab = Tab.empty
tab.tabfile = prefix/Tab::FILENAME
2021-05-28 11:24:51 -04:00
tab.source_modified_time = 1
2017-02-22 00:37:42 +01:00
tab.write
end
2021-05-28 11:24:51 -04:00
eligible_kegs = f.installed_kegs - [Keg.new(f.prefix("HEAD-111111_1")), Keg.new(f.prefix("0.1"))]
expect(f.eligible_kegs_for_cleanup.sort_by(&:version)).to eq(eligible_kegs.sort_by(&:version))
2017-02-22 00:37:42 +01:00
end
end
describe "#pour_bottle?" do
it "returns false if set to false" do
f = formula "foo" do
url "foo-1.0"
def pour_bottle?
false
end
end
expect(f).not_to pour_bottle
end
it "returns true if set to true" do
f = formula "foo" do
url "foo-1.0"
def pour_bottle?
true
end
end
expect(f).to pour_bottle
end
it "returns false if set to false via DSL" do
f = formula "foo" do
url "foo-1.0"
pour_bottle? do
reason "false reason"
satisfy { (var == etc) }
end
end
expect(f).not_to pour_bottle
end
it "returns true if set to true via DSL" do
f = formula "foo" do
url "foo-1.0"
pour_bottle? do
reason "true reason"
satisfy { true }
end
end
expect(f).to pour_bottle
end
it "returns false with `only_if: :clt_installed` on macOS", :needs_macos do
# Pretend CLT is not installed
allow(MacOS::CLT).to receive(:installed?).and_return(false)
2021-05-15 14:51:11 -04:00
f = formula "foo" do
url "foo-1.0"
pour_bottle? only_if: :clt_installed
end
expect(f).not_to pour_bottle
end
it "returns true with `only_if: :clt_installed` on macOS", :needs_macos do
# Pretend CLT is installed
allow(MacOS::CLT).to receive(:installed?).and_return(true)
f = formula "foo" do
url "foo-1.0"
pour_bottle? only_if: :clt_installed
end
expect(f).to pour_bottle
end
it "returns true with `only_if: :clt_installed` on Linux", :needs_linux do
2021-05-18 01:55:06 -04:00
f = formula "foo" do
url "foo-1.0"
pour_bottle? only_if: :clt_installed
2021-05-18 01:55:06 -04:00
end
expect(f).to pour_bottle
end
it "throws an error if passed both a symbol and a block" do
expect do
formula "foo" do
url "foo-1.0"
pour_bottle? only_if: :clt_installed do
reason "true reason"
satisfy { true }
end
end
end.to raise_error(ArgumentError, "Do not pass both a preset condition and a block to `pour_bottle?`")
end
it "throws an error if passed an invalid symbol" do
expect do
formula "foo" do
url "foo-1.0"
pour_bottle? only_if: :foo
end
end.to raise_error(ArgumentError, "Invalid preset `pour_bottle?` condition")
end
2017-02-22 00:37:42 +01:00
end
describe "alias changes" do
let(:f) do
formula "formula_name", alias_path: alias_path do
url "foo-1.0"
end
end
let(:new_formula) do
formula "new_formula_name", alias_path: alias_path do
url "foo-1.1"
end
end
let(:tab) { Tab.empty }
let(:alias_path) { "#{CoreTap.instance.alias_dir}/bar" }
2023-03-01 09:44:57 +00:00
let(:alias_name) { "bar" }
2017-02-22 00:37:42 +01:00
before do
2017-02-22 00:37:42 +01:00
allow(described_class).to receive(:installed).and_return([f])
f.build = tab
new_formula.build = tab
end
specify "alias changes when not installed with alias" do
tab.source["path"] = Formulary.core_path(f.name).to_s
expect(f.current_installed_alias_target).to be_nil
2017-02-22 00:37:42 +01:00
expect(f.latest_formula).to eq(f)
expect(f).not_to have_changed_installed_alias_target
expect(f).not_to supersede_an_installed_formula
expect(f).not_to have_changed_alias
expect(f.old_installed_formulae).to be_empty
end
specify "alias changes when not changed" do
tab.source["path"] = alias_path
2023-03-01 09:44:57 +00:00
stub_formula_loader(f, alias_name)
2017-02-22 00:37:42 +01:00
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf f.path, alias_path
2017-02-22 00:37:42 +01:00
expect(f.current_installed_alias_target).to eq(f)
expect(f.latest_formula).to eq(f)
expect(f).not_to have_changed_installed_alias_target
expect(f).not_to supersede_an_installed_formula
expect(f).not_to have_changed_alias
expect(f.old_installed_formulae).to be_empty
end
specify "alias changes when new alias target" do
tab.source["path"] = alias_path
2023-03-01 09:44:57 +00:00
stub_formula_loader(new_formula, alias_name)
2017-02-22 00:37:42 +01:00
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf new_formula.path, alias_path
2017-02-22 00:37:42 +01:00
expect(f.current_installed_alias_target).to eq(new_formula)
expect(f.latest_formula).to eq(new_formula)
expect(f).to have_changed_installed_alias_target
expect(f).not_to supersede_an_installed_formula
expect(f).to have_changed_alias
expect(f.old_installed_formulae).to be_empty
end
specify "alias changes when old formulae installed" do
tab.source["path"] = alias_path
2023-03-01 09:44:57 +00:00
stub_formula_loader(new_formula, alias_name)
2017-02-22 00:37:42 +01:00
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf new_formula.path, alias_path
2017-02-22 00:37:42 +01:00
expect(new_formula.current_installed_alias_target).to eq(new_formula)
expect(new_formula.latest_formula).to eq(new_formula)
expect(new_formula).not_to have_changed_installed_alias_target
expect(new_formula).to supersede_an_installed_formula
expect(new_formula).to have_changed_alias
expect(new_formula.old_installed_formulae).to eq([f])
end
end
describe "#outdated_kegs" do
let(:outdated_prefix) { (HOMEBREW_CELLAR/"#{f.name}/1.11") }
let(:same_prefix) { (HOMEBREW_CELLAR/"#{f.name}/1.20") }
let(:greater_prefix) { (HOMEBREW_CELLAR/"#{f.name}/1.21") }
let(:head_prefix) { (HOMEBREW_CELLAR/"#{f.name}/HEAD") }
let(:old_alias_target_prefix) { (HOMEBREW_CELLAR/"#{old_formula.name}/1.0") }
let(:f) do
formula do
url "foo"
version "1.20"
end
end
let(:old_formula) do
formula "foo@1" do
url "foo-1.0"
end
end
let(:new_formula) do
formula "foo@2" do
url "foo-2.0"
end
end
let(:alias_path) { "#{f.tap.alias_dir}/bar" }
2023-03-01 09:44:57 +00:00
let(:alias_name) { "bar" }
2017-02-22 00:37:42 +01:00
def setup_tab_for_prefix(prefix, options = {})
prefix.mkpath
tab = Tab.empty
tab.tabfile = prefix/Tab::FILENAME
tab.source["path"] = options[:path].to_s if options[:path]
tab.source["tap"] = options[:tap] if options[:tap]
tab.source["versions"] = options[:versions] if options[:versions]
tab.source_modified_time = options[:source_modified_time].to_i
tab.write unless options[:no_write]
tab
end
example "greater different tap installed" do
setup_tab_for_prefix(greater_prefix, tap: "user/repo")
expect(f.outdated_kegs).to be_empty
end
example "greater same tap installed" do
f.instance_variable_set(:@tap, CoreTap.instance)
setup_tab_for_prefix(greater_prefix, tap: "homebrew/core")
expect(f.outdated_kegs).to be_empty
end
example "outdated different tap installed" do
setup_tab_for_prefix(outdated_prefix, tap: "user/repo")
expect(f.outdated_kegs).not_to be_empty
end
example "outdated same tap installed" do
f.instance_variable_set(:@tap, CoreTap.instance)
setup_tab_for_prefix(outdated_prefix, tap: "homebrew/core")
expect(f.outdated_kegs).not_to be_empty
end
example "outdated follow alias and alias unchanged" do
f.follow_installed_alias = true
f.build = setup_tab_for_prefix(same_prefix, path: alias_path)
2023-03-01 09:44:57 +00:00
stub_formula_loader(f, alias_name)
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).to be_empty
end
example "outdated follow alias and alias changed and new target not installed" do
f.follow_installed_alias = true
f.build = setup_tab_for_prefix(same_prefix, path: alias_path)
2023-03-01 09:44:57 +00:00
stub_formula_loader(new_formula, alias_name)
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf new_formula.path, alias_path
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).not_to be_empty
end
example "outdated follow alias and alias changed and new target installed" do
f.follow_installed_alias = true
f.build = setup_tab_for_prefix(same_prefix, path: alias_path)
2023-03-01 09:44:57 +00:00
stub_formula_loader(new_formula, alias_name)
2017-02-22 00:37:42 +01:00
setup_tab_for_prefix(new_formula.prefix)
expect(f.outdated_kegs).to be_empty
end
example "outdated no follow alias and alias unchanged" do
f.follow_installed_alias = false
f.build = setup_tab_for_prefix(same_prefix, path: alias_path)
2023-03-01 09:44:57 +00:00
stub_formula_loader(f, alias_name)
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).to be_empty
end
example "outdated no follow alias and alias changed" do
f.follow_installed_alias = false
f.build = setup_tab_for_prefix(same_prefix, path: alias_path)
f2 = formula "foo@2" do
url "foo-2.0"
end
stub_formula_loader(f2, alias_path)
expect(f.outdated_kegs).to be_empty
end
example "outdated old alias targets installed" do
f = formula alias_path: alias_path do
url "foo-1.0"
end
tab = setup_tab_for_prefix(old_alias_target_prefix, path: alias_path)
old_formula.build = tab
allow(described_class).to receive(:installed).and_return([old_formula])
CoreTap.instance.alias_dir.mkpath
FileUtils.ln_sf f.path, alias_path
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).not_to be_empty
end
example "outdated old alias targets not installed" do
f = formula alias_path: alias_path do
url "foo-1.0"
end
tab = setup_tab_for_prefix(old_alias_target_prefix, path: old_formula.path)
old_formula.build = tab
allow(described_class).to receive(:installed).and_return([old_formula])
expect(f.outdated_kegs).to be_empty
end
example "outdated same head installed" do
f.instance_variable_set(:@tap, CoreTap.instance)
setup_tab_for_prefix(head_prefix, tap: "homebrew/core")
expect(f.outdated_kegs).to be_empty
end
example "outdated different head installed" do
f.instance_variable_set(:@tap, CoreTap.instance)
setup_tab_for_prefix(head_prefix, tap: "user/repo")
expect(f.outdated_kegs).to be_empty
end
example "outdated mixed taps greater version installed" do
f.instance_variable_set(:@tap, CoreTap.instance)
setup_tab_for_prefix(outdated_prefix, tap: "homebrew/core")
setup_tab_for_prefix(greater_prefix, tap: "user/repo")
expect(f.outdated_kegs).to be_empty
setup_tab_for_prefix(greater_prefix, tap: "homebrew/core")
described_class.clear_cache
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).to be_empty
end
example "outdated mixed taps outdated version installed" do
f.instance_variable_set(:@tap, CoreTap.instance)
extra_outdated_prefix = HOMEBREW_CELLAR/f.name/"1.0"
setup_tab_for_prefix(outdated_prefix)
setup_tab_for_prefix(extra_outdated_prefix, tap: "homebrew/core")
described_class.clear_cache
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).not_to be_empty
setup_tab_for_prefix(outdated_prefix, tap: "user/repo")
described_class.clear_cache
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).not_to be_empty
end
example "outdated same version tap installed" do
f.instance_variable_set(:@tap, CoreTap.instance)
setup_tab_for_prefix(same_prefix, tap: "homebrew/core")
expect(f.outdated_kegs).to be_empty
setup_tab_for_prefix(same_prefix, tap: "user/repo")
described_class.clear_cache
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).to be_empty
end
example "outdated installed head less than stable" do
tab = setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0" })
expect(f.outdated_kegs).not_to be_empty
tab.source["versions"] = { "stable" => f.version.to_s }
tab.write
described_class.clear_cache
2017-02-22 00:37:42 +01:00
expect(f.outdated_kegs).to be_empty
end
describe ":fetch_head" do
let(:f) do
repo = testball_repo
formula "testball" do
url "foo"
version "2.10"
head "file://#{repo}", using: :git
end
end
let(:testball_repo) { HOMEBREW_PREFIX/"testball_repo" }
example do
outdated_stable_prefix = HOMEBREW_CELLAR/"testball/1.0"
head_prefix_a = HOMEBREW_CELLAR/"testball/HEAD"
head_prefix_b = HOMEBREW_CELLAR/"testball/HEAD-aaaaaaa_1"
head_prefix_c = HOMEBREW_CELLAR/"testball/HEAD-18a7103"
setup_tab_for_prefix(outdated_stable_prefix)
tab_a = setup_tab_for_prefix(head_prefix_a, versions: { "stable" => "1.0" })
setup_tab_for_prefix(head_prefix_b)
testball_repo.mkdir
testball_repo.cd do
FileUtils.touch "LICENSE"
system("git", "-c", "init.defaultBranch=master", "init")
system("git", "add", "--all")
system("git", "commit", "-m", "Initial commit")
2017-02-22 00:37:42 +01:00
end
expect(f.outdated_kegs(fetch_head: true)).not_to be_empty
tab_a.source["versions"] = { "stable" => f.version.to_s }
tab_a.write
described_class.clear_cache
expect(f.outdated_kegs(fetch_head: true)).not_to be_empty
head_prefix_a.rmtree
described_class.clear_cache
expect(f.outdated_kegs(fetch_head: true)).not_to be_empty
setup_tab_for_prefix(head_prefix_c, source_modified_time: 1)
described_class.clear_cache
expect(f.outdated_kegs(fetch_head: true)).to be_empty
ensure
testball_repo.rmtree if testball_repo.exist?
2017-02-22 00:37:42 +01:00
end
end
describe "#mkdir" do
let(:dst) { mktmpdir }
it "creates intermediate directories" do
f.mkdir dst/"foo/bar/baz" do
expect(dst/"foo/bar/baz").to exist, "foo/bar/baz was not created"
expect(dst/"foo/bar/baz").to be_a_directory, "foo/bar/baz was not a directory structure"
end
end
end
2017-02-22 00:37:42 +01:00
describe "with changed version scheme" do
let(:f) do
formula "testball" do
url "foo"
version "20141010"
version_scheme 1
end
end
example do
prefix = HOMEBREW_CELLAR/"testball/0.1"
setup_tab_for_prefix(prefix, versions: { "stable" => "0.1" })
expect(f.outdated_kegs).not_to be_empty
end
end
describe "with mixed version schemes" do
let(:f) do
formula "testball" do
url "foo"
version "20141010"
version_scheme 3
end
end
example do
prefix_a = HOMEBREW_CELLAR/"testball/20141009"
setup_tab_for_prefix(prefix_a, versions: { "stable" => "20141009", "version_scheme" => 1 })
prefix_b = HOMEBREW_CELLAR/"testball/2.14"
setup_tab_for_prefix(prefix_b, versions: { "stable" => "2.14", "version_scheme" => 2 })
expect(f.outdated_kegs).not_to be_empty
described_class.clear_cache
2017-02-22 00:37:42 +01:00
prefix_c = HOMEBREW_CELLAR/"testball/20141009"
setup_tab_for_prefix(prefix_c, versions: { "stable" => "20141009", "version_scheme" => 3 })
expect(f.outdated_kegs).not_to be_empty
described_class.clear_cache
2017-02-22 00:37:42 +01:00
prefix_d = HOMEBREW_CELLAR/"testball/20141011"
setup_tab_for_prefix(prefix_d, versions: { "stable" => "20141009", "version_scheme" => 3 })
expect(f.outdated_kegs).to be_empty
end
end
describe "with version scheme" do
let(:f) do
formula "testball" do
url "foo"
version "1.0"
version_scheme 2
end
end
example do
head_prefix = HOMEBREW_CELLAR/"testball/HEAD"
setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0", "version_scheme" => 1 })
expect(f.outdated_kegs).not_to be_empty
described_class.clear_cache
2017-02-22 00:37:42 +01:00
head_prefix.rmtree
setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0", "version_scheme" => 2 })
expect(f.outdated_kegs).to be_empty
end
end
end
2020-08-31 10:59:27 -07:00
describe "#any_installed_version" do
let(:f) do
Class.new(Testball) do
version "1.0"
revision 1
end.new
end
it "returns nil when not installed" do
expect(f.any_installed_version).to be_nil
2020-08-31 10:59:27 -07:00
end
it "returns package version when installed" do
f.brew { f.install }
expect(f.any_installed_version).to eq(PkgVersion.parse("1.0_1"))
end
end
2022-02-24 12:31:32 +00:00
describe "#on_macos", :needs_macos do
let(:f) do
Class.new(Testball) do
attr_reader :test
def install
@test = 0
2022-02-24 12:31:32 +00:00
on_macos do
@test = 1
end
on_linux do
@test = 2
end
end
end.new
end
it "only calls code within on_macos" do
f.brew { f.install }
expect(f.test).to eq(1)
end
end
describe "#on_linux", :needs_linux do
let(:f) do
Class.new(Testball) do
attr_reader :test
def install
@test = 0
2022-02-24 12:31:32 +00:00
on_macos do
@test = 1
end
on_linux do
@test = 2
end
end
end.new
end
it "only calls code within on_linux" do
f.brew { f.install }
expect(f.test).to eq(2)
end
end
describe "#on_system" do
let(:f) do
Class.new(Testball) do
attr_reader :foo
attr_reader :bar
def install
@foo = 0
@bar = 0
on_system :linux, macos: :monterey do
@foo = 1
end
on_system :linux, macos: :big_sur_or_older do
@bar = 1
end
end
end.new
end
2022-07-11 04:03:37 -04:00
it "doesn't call code on Ventura", :needs_macos do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :ventura do
f.brew { f.install }
expect(f.foo).to eq(0)
expect(f.bar).to eq(0)
end
end
2022-07-11 04:03:37 -04:00
it "calls code on Linux", :needs_linux do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :linux do
f.brew { f.install }
expect(f.foo).to eq(1)
expect(f.bar).to eq(1)
end
end
2022-07-11 04:03:37 -04:00
it "calls code within `on_system :linux, macos: :monterey` on Monterey", :needs_macos do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :monterey do
f.brew { f.install }
expect(f.foo).to eq(1)
expect(f.bar).to eq(0)
end
end
2022-07-11 04:03:37 -04:00
it "calls code within `on_system :linux, macos: :big_sur_or_older` on Big Sur", :needs_macos do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :big_sur do
f.brew { f.install }
expect(f.foo).to eq(0)
expect(f.bar).to eq(1)
end
end
2022-07-11 04:03:37 -04:00
it "calls code within `on_system :linux, macos: :big_sur_or_older` on Catalina", :needs_macos do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :catalina do
f.brew { f.install }
expect(f.foo).to eq(0)
expect(f.bar).to eq(1)
end
end
end
describe "on_{os_version} blocks", :needs_macos do
let(:f) do
Class.new(Testball) do
attr_reader :test
def install
@test = 0
on_monterey :or_newer do
@test = 1
end
on_big_sur do
@test = 2
end
on_catalina :or_older do
@test = 3
end
end
end.new
end
it "only calls code within `on_monterey`" do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :monterey do
f.brew { f.install }
expect(f.test).to eq(1)
end
end
it "only calls code within `on_monterey :or_newer`" do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :ventura do
f.brew { f.install }
expect(f.test).to eq(1)
end
end
it "only calls code within `on_big_sur`" do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :big_sur do
f.brew { f.install }
expect(f.test).to eq(2)
end
end
it "only calls code within `on_catalina`" do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :catalina do
f.brew { f.install }
expect(f.test).to eq(3)
end
end
it "only calls code within `on_catalina :or_older`" do
2023-05-13 22:35:08 +02:00
Homebrew::SimulateSystem.with os: :mojave do
f.brew { f.install }
expect(f.test).to eq(3)
end
end
end
describe "#on_arm" do
before do
allow(Hardware::CPU).to receive(:type).and_return(:arm)
end
let(:f) do
Class.new(Testball) do
attr_reader :test
def install
@test = 0
on_arm do
@test = 1
end
on_intel do
@test = 2
end
end
end.new
end
it "only calls code within on_arm" do
f.brew { f.install }
expect(f.test).to eq(1)
end
end
describe "#on_intel" do
before do
allow(Hardware::CPU).to receive(:type).and_return(:intel)
end
let(:f) do
Class.new(Testball) do
attr_reader :test
def install
@test = 0
on_arm do
@test = 1
end
on_intel do
@test = 2
end
end
end.new
end
it "only calls code within on_intel" do
f.brew { f.install }
expect(f.test).to eq(2)
end
end
2022-08-10 19:07:19 +02:00
describe "#generate_completions_from_executable" do
let(:f) do
Class.new(Testball) do
def install
bin.mkpath
(bin/"foo").write <<-EOF
echo completion
EOF
FileUtils.chmod "+x", bin/"foo"
generate_completions_from_executable(bin/"foo", "test")
end
end.new
end
it "generates completion scripts" do
f.brew { f.install }
2022-08-10 20:21:05 +02:00
expect(f.bash_completion/"testball").to be_a_file
expect(f.zsh_completion/"_testball").to be_a_file
expect(f.fish_completion/"testball.fish").to be_a_file
2022-08-10 19:07:19 +02:00
end
end
2017-02-22 00:37:42 +01:00
end