diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb new file mode 100644 index 0000000000..1e064912fa --- /dev/null +++ b/Library/Homebrew/test/formula_spec.rb @@ -0,0 +1,1295 @@ +require "test/support/fixtures/testball" +require "formula" + +RSpec::Matchers.alias_matcher :follow_installed_alias, :be_follow_installed_alias +RSpec::Matchers.alias_matcher :have_any_version_installed, :be_any_version_installed +RSpec::Matchers.alias_matcher :need_migration, :be_migration_needed + +RSpec::Matchers.alias_matcher :have_changed_installed_alias_target, :be_installed_alias_target_changed +RSpec::Matchers.alias_matcher :supersede_an_installed_formula, :be_supersedes_an_installed_formula +RSpec::Matchers.alias_matcher :have_changed_alias, :be_alias_changed + +RSpec::Matchers.alias_matcher :have_option_defined, :be_option_defined +RSpec::Matchers.alias_matcher :have_post_install_defined, :be_post_install_defined +RSpec::Matchers.alias_matcher :have_test_defined, :be_test_defined +RSpec::Matchers.alias_matcher :pour_bottle, :be_pour_bottle + +describe Formula do + describe "::new" do + let(:klass) do + Class.new(described_class) do + url "http://example.com/foo-1.0.tar.gz" + 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 } + 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 { 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.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 "in a Tap" do + 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 { 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.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 + + example "installed alias with core" do + f = formula do + url "foo-1.0" + end + + build_values_with_no_installed_alias = [ + nil, + BuildOptions.new({}, {}), + 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 + 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}" + + 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" + f = formula name, path: path do + url "foo-1.0" + end + + build_values_with_no_installed_alias = [nil, BuildOptions.new({}, {}), Tab.new(source: { "path" => f.path })] + 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 + 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}" + + 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) + end + + specify "#prefix" do + f = Testball.new + expect(f.prefix).to eq(HOMEBREW_CELLAR/f.name/"0.1") + expect(f.prefix).to be_kind_of(Pathname) + 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") + f.instance_variable_set(:@oldname, "oldname") + 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 "#installed?" do + let(:f) { Testball.new } + + it "returns false if the #installed_prefix is not a directory" do + allow(f).to receive(:installed_prefix).and_return(double(directory?: false)) + expect(f).not_to be_installed + end + + it "returns false if the #installed_prefix does not have children" do + allow(f).to receive(:installed_prefix).and_return(double(directory?: true, children: [])) + expect(f).not_to be_installed + end + + it "returns true if the #installed_prefix has children" do + allow(f).to receive(:installed_prefix).and_return(double(directory?: true, children: [double])) + expect(f).to be_installed + end + end + + describe "#installed prefix" do + let(:f) do + formula do + url "foo" + version "1.9" + + head "foo" + + devel do + url "foo" + version "2.1-devel" + end + end + end + + let(:stable_prefix) { HOMEBREW_CELLAR/f.name/f.version } + let(:devel_prefix) { HOMEBREW_CELLAR/f.name/f.devel.version } + let(:head_prefix) { HOMEBREW_CELLAR/f.name/f.head.version } + + it "is the same as #prefix by default" do + expect(f.installed_prefix).to eq(f.prefix) + end + + it "returns the stable prefix if it is installed" do + stable_prefix.mkpath + expect(f.installed_prefix).to eq(stable_prefix) + end + + it "returns the devel prefix if it is installed" do + devel_prefix.mkpath + expect(f.installed_prefix).to eq(devel_prefix) + end + + it "returns the head prefix if it is installed" do + head_prefix.mkpath + expect(f.installed_prefix).to eq(head_prefix) + 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.installed_prefix).to eq(stable_prefix) + end + + it "returns the stable prefix if head and devel are outdated" do + head_prefix.mkpath + + tab = Tab.empty + tab.tabfile = head_prefix/Tab::FILENAME + tab.source["versions"] = { "stable" => "1.9", "devel" => "2.0" } + tab.write + + expect(f.installed_prefix).to eq(stable_prefix) + end + + it "returns the devel prefix if the active specification is :devel" do + f.active_spec = :devel + expect(f.installed_prefix).to eq(devel_prefix) + end + + it "returns the head prefix if the active specification is :head" do + f.active_spec = :head + expect(f.installed_prefix).to eq(head_prefix) + 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}" + version << "_#{revision}" unless revision.zero? + + 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 + 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({}, {}) + + expect(f.alias_path).to eq(alias_path) + expect(f.installed_alias_path).to be nil + end + + example "alias paths with tab with non alias source path" do + alias_path = (CoreTap.instance.alias_dir/"another_name") + source_path = (CoreTap.instance.formula_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 }) + + expect(f.alias_path).to eq(alias_path) + expect(f.installed_alias_path).to be nil + 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 }) + + expect(f.alias_path).to eq(alias_path) + expect(f.installed_alias_path).to eq(source_path.to_s) + end + end + + 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) + + expect(described_class.installed_with_alias_path(alias_path)) + .to eq([formula_with_alias]) + end + end + + specify "spec integration" do + f = formula do + homepage "http://example.com" + + url "http://example.com/test-0.1.tbz" + mirror "http://example.org/test-0.1.tbz" + sha256 TEST_SHA256 + + head "http://example.com/test.git", tag: "foo" + + devel do + url "http://example.com/test-0.2.tbz" + mirror "http://example.org/test-0.2.tbz" + sha256 TEST_SHA256 + end + end + + expect(f.homepage).to eq("http://example.com") + expect(f.version).to eq(Version.create("0.1")) + expect(f).to be_stable + expect(f.stable.version).to eq(Version.create("0.1")) + expect(f.devel.version).to eq(Version.create("0.2")) + expect(f.head.version).to eq(Version.create("HEAD")) + end + + specify "#active_spec=" do + f = formula do + url "foo" + version "1.0" + revision 1 + + devel do + url "foo" + version "1.0beta" + end + 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") + + f.active_spec = :devel + + expect(f.active_spec_sym).to eq(:devel) + expect(f.send(:active_spec)).to eq(f.devel) + expect(f.pkg_version.to_s).to eq("1.0beta_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 + + expect(f.class.stable).to be_kind_of(SoftwareSpec) + expect(f.class.devel).to be_kind_of(SoftwareSpec) + expect(f.class.head).to be_kind_of(SoftwareSpec) + end + + specify "incomplete instance specs are not accessible" do + f = formula do + url "foo-1.0" + end + + expect(f.devel).to be nil + expect(f.head).to be nil + end + + it "honors attributes declared before specs" do + f = formula do + url "foo-1.0" + + depends_on "foo" + + devel do + url "foo-1.1" + end + end + + expect(f.class.stable.deps.first.name).to eq("foo") + expect(f.class.devel.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" + + shutup do + system("git", "init") + system("git", "add", "--all") + system("git", "commit", "-m", "Initial commit") + end + end + + f.update_head_version + + expect(f.head.version).to eq(Version.create("HEAD-5658946")) + end + + specify "legacy options" do + f = formula do + url "foo-1.0" + + def options + [ + ["--foo", "desc"], + ["--bar", "desc"], + ] + end + + option("baz") + end + + expect(f).to have_option_defined("foo") + expect(f).to have_option_defined("bar") + expect(f).to have_option_defined("baz") + 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 + + specify "#test_defined?" do + f1 = formula do + url "foo-1.0" + + def test + # do nothing + end + end + + f2 = formula do + url "foo-1.0" + end + + expect(f1).to have_test_defined + expect(f2).not_to have_test_defined + end + + 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 + + specify "dependencies" do + 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"]) + expect(f5.recursive_dependencies.map(&:name)).to eq(["f1", "f2", "f3", "f4"]) + expect(f5.runtime_dependencies.map(&:name)).to eq(["f1", "f4"]) + end + + specify "runtime dependencies with optional deps from tap" do + tap_loader = double + + 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) + stub_formula_loader(formula("f2") { url("f2-1.0") }, "baz/qux/f2") + + 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"]) + + stub_formula_loader(formula("f1") { 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"]) + end + + specify "requirements" do + f1 = formula "f1" do + url "f1-1" + + depends_on :python + depends_on x11: :recommended + depends_on xcode: ["1.0", :optional] + end + stub_formula_loader(f1) + + python = PythonRequirement.new + x11 = X11Requirement.new("x11", [:recommended]) + xcode = XcodeRequirement.new(["1.0", :optional]) + + expect(Set.new(f1.recursive_requirements)).to eq(Set[python, x11]) + + f1.build = BuildOptions.new(["--with-xcode", "--without-x11"], f1.options) + + expect(Set.new(f1.recursive_requirements)).to eq(Set[python, xcode]) + + 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[python, x11]) + expect(Set.new(f2.recursive_requirements {})).to eq(Set[python, x11, xcode]) + + requirements = f2.recursive_requirements do |_dependent, requirement| + Requirement.prune if requirement.is_a?(PythonRequirement) + end + + expect(Set.new(requirements)).to eq(Set[x11, xcode]) + end + + specify "#to_hash" do + f1 = formula "foo" do + url "foo-1.0" + + bottle do + cellar(:any) + sha256(TEST_SHA256 => Utils::Bottles.tag) + 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["versions"]["stable"]).to eq("1.0") + expect(h["versions"]["bottle"]).to be_truthy + end + + 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 + + shutup do + [f1, f2, f3, f4].each do |f| + f.brew { f.install } + Tab.create(f, DevelopmentTools.default_compiler, :libcxx).write + end + end + + expect(f1).to be_installed + expect(f2).to be_installed + expect(f3).to be_installed + expect(f4).to be_installed + 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 + + shutup do + f1.brew { f1.install } + f1.pin + f2.brew { f2.install } + f3.brew { f3.install } + end + + expect(f1.prefix).to eq((HOMEBREW_PINNED_KEGS/f1.name).resolved_path) + expect(f1).to be_installed + expect(f2).to be_installed + expect(f3).to be_installed + expect(shutup { f3.eligible_kegs_for_cleanup }).to eq([Keg.new(f2.prefix)]) + end + + specify "with HEAD installed" do + f = formula do + version("0.1") + head("foo") + end + + stable_prefix = f.installed_prefix + stable_prefix.mkpath + + [["000000_1", 1], ["111111", 2], ["111111_1", 2]].each do |pkg_version_suffix, stamp| + prefix = f.prefix("HEAD-#{pkg_version_suffix}") + prefix.mkpath + tab = Tab.empty + tab.tabfile = prefix/Tab::FILENAME + tab.source_modified_time = stamp + tab.write + end + + eligible_kegs = f.installed_kegs - [Keg.new(f.prefix("HEAD-111111_1"))] + expect(f.eligible_kegs_for_cleanup).to eq(eligible_kegs) + 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 + 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" } + + before(:each) do + 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 + 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 + stub_formula_loader(f, alias_path) + + 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 + stub_formula_loader(new_formula, alias_path) + + 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 + stub_formula_loader(new_formula, alias_path) + + 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" } + + 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 + + def reset_outdated_kegs + f.instance_variable_set(:@outdated_kegs, nil) + 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) + stub_formula_loader(f, alias_path) + 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) + stub_formula_loader(new_formula, alias_path) + 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) + stub_formula_loader(new_formula, alias_path) + 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) + stub_formula_loader(f, alias_path) + 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]) + 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") + reset_outdated_kegs + + 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") + reset_outdated_kegs + + expect(f.outdated_kegs).not_to be_empty + + setup_tab_for_prefix(outdated_prefix, tap: "user/repo") + reset_outdated_kegs + + 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") + reset_outdated_kegs + + 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 + reset_outdated_kegs + + 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 + begin + 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" + + shutup do + system("git", "init") + system("git", "add", "--all") + system("git", "commit", "-m", "Initial commit") + end + end + + expect(f.outdated_kegs(fetch_head: true)).not_to be_empty + + tab_a.source["versions"] = { "stable" => f.version.to_s } + tab_a.write + reset_outdated_kegs + expect(f.outdated_kegs(fetch_head: true)).not_to be_empty + + head_prefix_a.rmtree + reset_outdated_kegs + expect(f.outdated_kegs(fetch_head: true)).not_to be_empty + + setup_tab_for_prefix(head_prefix_c, source_modified_time: 1) + reset_outdated_kegs + expect(f.outdated_kegs(fetch_head: true)).to be_empty + ensure + testball_repo.rmtree if testball_repo.exist? + end + end + end + + 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 + reset_outdated_kegs + + 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 + reset_outdated_kegs + + 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 + + reset_outdated_kegs + 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 +end diff --git a/Library/Homebrew/test/formula_test.rb b/Library/Homebrew/test/formula_test.rb deleted file mode 100644 index 318175d49c..0000000000 --- a/Library/Homebrew/test/formula_test.rb +++ /dev/null @@ -1,1205 +0,0 @@ -require "testing_env" -require "test/support/fixtures/testball" -require "formula" - -class FormulaTests < Homebrew::TestCase - def test_formula_instantiation - klass = Class.new(Formula) { url "http://example.com/foo-1.0.tar.gz" } - name = "formula_name" - path = Formulary.core_path(name) - spec = :stable - - f = klass.new(name, path, spec) - assert_equal name, f.name - assert_equal name, f.specified_name - assert_equal name, f.full_name - assert_equal name, f.full_specified_name - assert_equal path, f.path - assert_nil f.alias_path - assert_nil f.alias_name - assert_nil f.full_alias_name - assert_raises(ArgumentError) { klass.new } - end - - def test_formula_instantiation_with_alias - klass = Class.new(Formula) { url "http://example.com/foo-1.0.tar.gz" } - name = "formula_name" - path = Formulary.core_path(name) - spec = :stable - alias_name = "baz@1" - alias_path = CoreTap.instance.alias_dir/alias_name - - f = klass.new(name, path, spec, alias_path: alias_path) - assert_equal name, f.name - assert_equal name, f.full_name - assert_equal path, f.path - assert_equal alias_path, f.alias_path - assert_equal alias_name, f.alias_name - assert_equal alias_name, f.specified_name - assert_equal alias_name, f.full_alias_name - assert_equal alias_name, f.full_specified_name - assert_raises(ArgumentError) { klass.new } - end - - def test_tap_formula_instantiation - tap = Tap.new("foo", "bar") - klass = Class.new(Formula) { url "baz-1.0" } - name = "baz" - full_name = "#{tap.user}/#{tap.repo}/#{name}" - path = tap.path/"Formula/#{name}.rb" - spec = :stable - - f = klass.new(name, path, spec) - assert_equal name, f.name - assert_equal name, f.specified_name - assert_equal full_name, f.full_name - assert_equal full_name, f.full_specified_name - assert_equal path, f.path - assert_nil f.alias_path - assert_nil f.alias_name - assert_nil f.full_alias_name - assert_raises(ArgumentError) { klass.new } - end - - def test_tap_formula_instantiation_with_alias - tap = Tap.new("foo", "bar") - klass = Class.new(Formula) { url "baz-1.0" } - name = "baz" - full_name = "#{tap.user}/#{tap.repo}/#{name}" - path = tap.path/"Formula/#{name}.rb" - spec = :stable - alias_name = "baz@1" - full_alias_name = "#{tap.user}/#{tap.repo}/#{alias_name}" - alias_path = CoreTap.instance.alias_dir/alias_name - - f = klass.new(name, path, spec, alias_path: alias_path) - assert_equal name, f.name - assert_equal full_name, f.full_name - assert_equal path, f.path - assert_equal alias_path, f.alias_path - assert_equal alias_name, f.alias_name - assert_equal alias_name, f.specified_name - assert_equal full_alias_name, f.full_alias_name - assert_equal full_alias_name, f.full_specified_name - assert_raises(ArgumentError) { klass.new } - end - - def test_follow_installed_alias - f = formula { url "foo-1.0" } - assert_predicate f, :follow_installed_alias? - - f.follow_installed_alias = true - assert_predicate f, :follow_installed_alias? - - f.follow_installed_alias = false - refute_predicate f, :follow_installed_alias? - end - - def test_installed_alias_with_core - f = formula { url "foo-1.0" } - - build_values_with_no_installed_alias = [ - nil, - BuildOptions.new({}, {}), - Tab.new(source: { "path" => f.path.to_s }), - ] - - build_values_with_no_installed_alias.each do |build| - f.build = build - assert_nil f.installed_alias_path - assert_nil f.installed_alias_name - assert_nil f.full_installed_alias_name - assert_equal f.name, f.installed_specified_name - assert_equal f.name, f.full_installed_specified_name - end - - alias_name = "bar" - alias_path = "#{CoreTap.instance.alias_dir}/#{alias_name}" - f.build = Tab.new(source: { "path" => alias_path }) - assert_equal alias_path, f.installed_alias_path - assert_equal alias_name, f.installed_alias_name - assert_equal alias_name, f.full_installed_alias_name - assert_equal alias_name, f.installed_specified_name - assert_equal alias_name, f.full_installed_specified_name - end - - def test_installed_alias_with_tap - tap = Tap.new("user", "repo") - name = "foo" - path = "#{tap.path}/Formula/#{name}.rb" - f = formula(name, path) { url "foo-1.0" } - - build_values_with_no_installed_alias = [ - nil, - BuildOptions.new({}, {}), - Tab.new(source: { "path" => f.path }), - ] - - build_values_with_no_installed_alias.each do |build| - f.build = build - assert_nil f.installed_alias_path - assert_nil f.installed_alias_name - assert_nil f.full_installed_alias_name - assert_equal f.name, f.installed_specified_name - assert_equal f.full_name, f.full_installed_specified_name - end - - alias_name = "bar" - full_alias_name = "#{tap.user}/#{tap.repo}/#{alias_name}" - alias_path = "#{tap.alias_dir}/#{alias_name}" - f.build = Tab.new(source: { "path" => alias_path }) - assert_equal alias_path, f.installed_alias_path - assert_equal alias_name, f.installed_alias_name - assert_equal full_alias_name, f.full_installed_alias_name - assert_equal alias_name, f.installed_specified_name - assert_equal full_alias_name, f.full_installed_specified_name - end - - def test_prefix - f = Testball.new - assert_equal HOMEBREW_CELLAR/f.name/"0.1", f.prefix - assert_kind_of Pathname, f.prefix - end - - def test_revised_prefix - f = Class.new(Testball) { revision 1 }.new - assert_equal HOMEBREW_CELLAR/f.name/"0.1_1", f.prefix - end - - def test_any_version_installed? - f = formula do - url "foo" - version "1.0" - end - refute_predicate f, :any_version_installed? - prefix = HOMEBREW_CELLAR+f.name+"0.1" - prefix.mkpath - FileUtils.touch prefix+Tab::FILENAME - assert_predicate f, :any_version_installed? - end - - def test_migration_needed - f = Testball.new("newname") - f.instance_variable_set(:@oldname, "oldname") - 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.join("INSTALL_RECEIPT.json") - oldname_tab.write - - refute_predicate f, :migration_needed? - - oldname_tab.tabfile.unlink - oldname_tab.source["tap"] = "homebrew/core" - oldname_tab.write - - assert_predicate f, :migration_needed? - - newname_prefix.mkpath - - refute_predicate f, :migration_needed? - end - - def test_installed? - f = Testball.new - f.stubs(:installed_prefix).returns(stub(directory?: false)) - refute_predicate f, :installed? - - f.stubs(:installed_prefix).returns( - stub(directory?: true, children: []), - ) - refute_predicate f, :installed? - - f.stubs(:installed_prefix).returns( - stub(directory?: true, children: [stub]), - ) - assert_predicate f, :installed? - end - - def test_installed_prefix - f = Testball.new - assert_equal f.prefix, f.installed_prefix - end - - def test_installed_prefix_head_installed - f = formula do - head "foo" - devel do - url "foo" - version "1.0" - end - end - prefix = HOMEBREW_CELLAR+f.name+f.head.version - prefix.mkpath - assert_equal prefix, f.installed_prefix - end - - def test_installed_prefix_devel_installed - f = formula do - head "foo" - devel do - url "foo" - version "1.0" - end - end - prefix = HOMEBREW_CELLAR+f.name+f.devel.version - prefix.mkpath - assert_equal prefix, f.installed_prefix - end - - def test_installed_prefix_stable_installed - f = formula do - head "foo" - devel do - url "foo" - version "1.0-devel" - end - end - prefix = HOMEBREW_CELLAR+f.name+f.version - prefix.mkpath - assert_equal prefix, f.installed_prefix - end - - def test_installed_prefix_outdated_stable_head_installed - f = formula do - url "foo" - version "1.9" - head "foo" - end - - head_prefix = HOMEBREW_CELLAR/"#{f.name}/HEAD" - head_prefix.mkpath - tab = Tab.empty - tab.tabfile = head_prefix.join("INSTALL_RECEIPT.json") - tab.source["versions"] = { "stable" => "1.0" } - tab.write - - assert_equal HOMEBREW_CELLAR/"#{f.name}/#{f.version}", f.installed_prefix - end - - def test_installed_prefix_outdated_devel_head_installed - f = formula do - url "foo" - version "1.9" - devel do - url "foo" - version "2.1" - end - end - - head_prefix = HOMEBREW_CELLAR/"#{f.name}/HEAD" - head_prefix.mkpath - tab = Tab.empty - tab.tabfile = head_prefix.join("INSTALL_RECEIPT.json") - tab.source["versions"] = { "stable" => "1.9", "devel" => "2.0" } - tab.write - - assert_equal HOMEBREW_CELLAR/"#{f.name}/#{f.version}", f.installed_prefix - end - - def test_installed_prefix_head - f = formula("test", Pathname.new(__FILE__).expand_path, :head) do - head "foo" - devel do - url "foo" - version "1.0-devel" - end - end - prefix = HOMEBREW_CELLAR+f.name+f.head.version - assert_equal prefix, f.installed_prefix - end - - def test_installed_prefix_devel - f = formula("test", Pathname.new(__FILE__).expand_path, :devel) do - head "foo" - devel do - url "foo" - version "1.0-devel" - end - end - prefix = HOMEBREW_CELLAR+f.name+f.devel.version - assert_equal prefix, f.installed_prefix - end - - def test_latest_head_prefix - f = Testball.new - - stamps_with_revisions = [[111111, 1], [222222, 1], [222222, 2], [222222, 0]] - - stamps_with_revisions.each do |stamp, revision| - version = "HEAD-#{stamp}" - version += "_#{revision}" if revision > 0 - prefix = f.rack.join(version) - prefix.mkpath - - tab = Tab.empty - tab.tabfile = prefix.join("INSTALL_RECEIPT.json") - tab.source_modified_time = stamp - tab.write - end - - prefix = HOMEBREW_CELLAR/"#{f.name}/HEAD-222222_2" - assert_equal prefix, f.latest_head_prefix - end - - def test_equality - x = Testball.new - y = Testball.new - assert_equal x, y - assert_eql x, y - assert_equal x.hash, y.hash - end - - def test_inequality - x = Testball.new("foo") - y = Testball.new("bar") - refute_equal x, y - refute_eql x, y - refute_equal x.hash, y.hash - end - - def test_comparison_with_non_formula_objects_does_not_raise - refute_equal Testball.new, Object.new - end - - def test_sort_operator - assert_nil Testball.new <=> Object.new - end - - def test_alias_paths_with_build_options - alias_path = CoreTap.instance.alias_dir/"another_name" - f = formula(alias_path: alias_path) { url "foo-1.0" } - f.build = BuildOptions.new({}, {}) - assert_equal alias_path, f.alias_path - assert_nil f.installed_alias_path - end - - def test_alias_paths_with_tab_with_non_alias_source_path - alias_path = CoreTap.instance.alias_dir/"another_name" - source_path = CoreTap.instance.formula_dir/"another_other_name" - f = formula(alias_path: alias_path) { url "foo-1.0" } - f.build = Tab.new(source: { "path" => source_path.to_s }) - assert_equal alias_path, f.alias_path - assert_nil f.installed_alias_path - end - - def test_alias_paths_with_tab_with_alias_source_path - alias_path = CoreTap.instance.alias_dir/"another_name" - source_path = CoreTap.instance.alias_dir/"another_other_name" - f = formula(alias_path: alias_path) { url "foo-1.0" } - f.build = Tab.new(source: { "path" => source_path.to_s }) - assert_equal alias_path, f.alias_path - assert_equal source_path.to_s, f.installed_alias_path - end - - def test_installed_with_alias_path_with_nil - assert_predicate Formula.installed_with_alias_path(nil), :empty? - end - - def test_installed_with_alias_path_with_a_path - alias_path = "#{CoreTap.instance.alias_dir}/alias" - different_alias_path = "#{CoreTap.instance.alias_dir}/another_alias" - - formula_with_alias = formula("foo") { url "foo-1.0" } - formula_with_alias.build = Tab.empty - formula_with_alias.build.source["path"] = alias_path - - formula_without_alias = formula("bar") { url "bar-1.0" } - formula_without_alias.build = Tab.empty - formula_without_alias.build.source["path"] = formula_without_alias.path.to_s - - formula_with_different_alias = formula("baz") { url "baz-1.0" } - 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, - ] - - Formula.stubs(:installed).returns(formulae) - assert_equal [formula_with_alias], Formula.installed_with_alias_path(alias_path) - end - - def test_formula_spec_integration - f = formula do - homepage "http://example.com" - url "http://example.com/test-0.1.tbz" - mirror "http://example.org/test-0.1.tbz" - sha256 TEST_SHA256 - - head "http://example.com/test.git", tag: "foo" - - devel do - url "http://example.com/test-0.2.tbz" - mirror "http://example.org/test-0.2.tbz" - sha256 TEST_SHA256 - end - end - - assert_equal "http://example.com", f.homepage - assert_version_equal "0.1", f.version - assert_predicate f, :stable? - - assert_version_equal "0.1", f.stable.version - assert_version_equal "0.2", f.devel.version - assert_version_equal "HEAD", f.head.version - end - - def test_formula_active_spec= - f = formula do - url "foo" - version "1.0" - revision 1 - - devel do - url "foo" - version "1.0beta" - end - end - assert_equal :stable, f.active_spec_sym - assert_equal f.stable, f.send(:active_spec) - assert_equal "1.0_1", f.pkg_version.to_s - f.active_spec = :devel - assert_equal :devel, f.active_spec_sym - assert_equal f.devel, f.send(:active_spec) - assert_equal "1.0beta_1", f.pkg_version.to_s - assert_raises(FormulaSpecificationError) { f.active_spec = :head } - end - - def test_path - name = "foo-bar" - assert_equal Pathname.new("#{HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core/Formula/#{name}.rb"), Formulary.core_path(name) - end - - def test_class_specs_are_always_initialized - f = formula { url "foo-1.0" } - - %w[stable devel head].each do |spec| - assert_kind_of SoftwareSpec, f.class.send(spec) - end - end - - def test_incomplete_instance_specs_are_not_accessible - f = formula { url "foo-1.0" } - - %w[devel head].each { |spec| assert_nil f.send(spec) } - end - - def test_honors_attributes_declared_before_specs - f = formula do - url "foo-1.0" - depends_on "foo" - devel { url "foo-1.1" } - end - - %w[stable devel head].each do |spec| - assert_equal "foo", f.class.send(spec).deps.first.name - end - end - - def test_simple_version - assert_equal PkgVersion.parse("1.0"), formula { url "foo-1.0.bar" }.pkg_version - end - - def test_version_with_revision - f = formula do - url "foo-1.0.bar" - revision 1 - end - - assert_equal PkgVersion.parse("1.0_1"), f.pkg_version - end - - def test_head_uses_revisions - f = formula("test", Pathname.new(__FILE__).expand_path, :head) do - url "foo-1.0.bar" - revision 1 - head "foo" - end - - assert_equal PkgVersion.parse("HEAD_1"), f.pkg_version - end - - def test_update_head_version - 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" - shutup do - system "git", "init" - system "git", "add", "--all" - system "git", "commit", "-m", "Initial commit" - end - end - - f.update_head_version - assert_equal Version.create("HEAD-5658946"), f.head.version - end - - def test_legacy_options - f = formula do - url "foo-1.0" - - def options - [["--foo", "desc"], ["--bar", "desc"]] - end - - option "baz" - end - - assert f.option_defined?("foo") - assert f.option_defined?("bar") - assert f.option_defined?("baz") - end - - def test_desc - f = formula do - desc "a formula" - url "foo-1.0" - end - - assert_equal "a formula", f.desc - end - - def test_post_install_defined - f1 = formula do - url "foo-1.0" - - def post_install; end - end - - f2 = formula do - url "foo-1.0" - end - - assert f1.post_install_defined? - refute f2.post_install_defined? - end - - def test_test_defined - f1 = formula do - url "foo-1.0" - - def test; end - end - - f2 = formula do - url "foo-1.0" - end - - assert f1.test_defined? - refute f2.test_defined? - end - - def test_test_fixtures - f1 = formula do - url "foo-1.0" - end - - assert_equal Pathname.new("#{HOMEBREW_LIBRARY_PATH}/test/support/fixtures/foo"), - f1.test_fixtures("foo") - end - - def test_dependencies - stub_formula_loader formula("f1") { url "f1-1.0" } - stub_formula_loader formula("f2") { url "f2-1.0" } - - f3 = formula("f3") do - url "f3-1.0" - depends_on "f1" => :build - depends_on "f2" - end - stub_formula_loader f3 - - f4 = formula("f4") do - url "f4-1.0" - depends_on "f1" - end - stub_formula_loader f4 - - f5 = formula("f5") do - url "f5-1.0" - depends_on "f3" => :build - depends_on "f4" - end - - assert_equal %w[f3 f4], f5.deps.map(&:name) - assert_equal %w[f1 f2 f3 f4], f5.recursive_dependencies.map(&:name) - assert_equal %w[f1 f4], f5.runtime_dependencies.map(&:name) - end - - def test_runtime_dependencies_with_optional_deps_from_tap - tap_loader = mock - tap_loader.stubs(:get_formula).raises(RuntimeError, "tried resolving tap formula") - Formulary.stubs(:loader_for).with("foo/bar/f1", from: nil).returns(tap_loader) - - stub_formula_loader formula("f2") { url "f2-1.0" }, "baz/qux/f2" - - f3 = formula("f3") do - url "f3-1.0" - depends_on "foo/bar/f1" => :optional - depends_on "baz/qux/f2" - end - - # f1 shouldn't be loaded by default. - # If it is, an exception will be raised. - assert_equal %w[baz/qux/f2], f3.runtime_dependencies.map(&:name) - - # If --with-f1, f1 should be loaded. - stub_formula_loader formula("f1") { url "f1-1.0" }, "foo/bar/f1" - f3.build = BuildOptions.new(Options.create(%w[--with-f1]), f3.options) - assert_equal %w[foo/bar/f1 baz/qux/f2], f3.runtime_dependencies.map(&:name) - end - - def test_requirements - f1 = formula("f1") do - url "f1-1" - - depends_on :python - depends_on x11: :recommended - depends_on xcode: ["1.0", :optional] - end - stub_formula_loader f1 - - python = PythonRequirement.new - x11 = X11Requirement.new("x11", [:recommended]) - xcode = XcodeRequirement.new(["1.0", :optional]) - - # Default block should filter out deps that aren't being used - assert_equal Set[python, x11], Set.new(f1.recursive_requirements) - - f1.build = BuildOptions.new(["--with-xcode", "--without-x11"], f1.options) - assert_equal Set[python, xcode], Set.new(f1.recursive_requirements) - f1.build = f1.stable.build - - f2 = formula("f2") do - url "f2-1" - depends_on "f1" - end - - assert_equal Set[python, x11], Set.new(f2.recursive_requirements) - - # Empty block should allow all requirements - assert_equal Set[python, x11, xcode], Set.new(f2.recursive_requirements {}) - - # Requirements can be pruned - requirements = f2.recursive_requirements do |_dependent, requirement| - Requirement.prune if requirement.is_a?(PythonRequirement) - end - assert_equal Set[x11, xcode], Set.new(requirements) - end - - def test_to_hash - f1 = formula("foo") do - url "foo-1.0" - end - - h = f1.to_hash - assert h.is_a?(Hash), "Formula#to_hash should return a Hash" - assert_equal "foo", h["name"] - assert_equal "foo", h["full_name"] - assert_equal "1.0", h["versions"]["stable"] - end - - def test_to_hash_bottle - f1 = formula("foo") do - url "foo-1.0" - - bottle do - cellar :any - sha256 TEST_SHA256 => Utils::Bottles.tag - end - end - - h = f1.to_hash - assert h.is_a?(Hash), "Formula#to_hash should return a Hash" - assert h["versions"]["bottle"], "The hash should say the formula is bottled" - end - - def test_eligible_kegs_for_cleanup - 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 - - shutup do - [f1, f2, f3, f4].each do |f| - f.brew { f.install } - Tab.create(f, DevelopmentTools.default_compiler, :libcxx).write - end - end - - assert_predicate f1, :installed? - assert_predicate f2, :installed? - assert_predicate f3, :installed? - assert_predicate f4, :installed? - - assert_equal [f2, f1].map { |f| Keg.new(f.prefix) }, - f3.eligible_kegs_for_cleanup.sort_by(&:version) - end - - def test_eligible_kegs_for_cleanup_keg_pinned - f1 = Class.new(Testball) { version "0.1" }.new - f2 = Class.new(Testball) { version "0.2" }.new - f3 = Class.new(Testball) { version "0.3" }.new - - shutup do - f1.brew { f1.install } - f1.pin - f2.brew { f2.install } - f3.brew { f3.install } - end - - assert_equal (HOMEBREW_PINNED_KEGS/f1.name).resolved_path, f1.prefix - - assert_predicate f1, :installed? - assert_predicate f2, :installed? - assert_predicate f3, :installed? - - assert_equal [Keg.new(f2.prefix)], shutup { f3.eligible_kegs_for_cleanup } - end - - def test_eligible_kegs_for_cleanup_head_installed - f = formula do - version "0.1" - head "foo" - end - - stable_prefix = f.installed_prefix - stable_prefix.mkpath - - [["000000_1", 1], ["111111", 2], ["111111_1", 2]].each do |pkg_version_suffix, stamp| - prefix = f.prefix("HEAD-#{pkg_version_suffix}") - prefix.mkpath - tab = Tab.empty - tab.tabfile = prefix.join("INSTALL_RECEIPT.json") - tab.source_modified_time = stamp - tab.write - end - - eligible_kegs = f.installed_kegs - [Keg.new(f.prefix("HEAD-111111_1"))] - assert_equal eligible_kegs, f.eligible_kegs_for_cleanup - end - - def test_pour_bottle - f_false = formula("foo") do - url "foo-1.0" - def pour_bottle? - false - end - end - refute f_false.pour_bottle? - - f_true = formula("foo") do - url "foo-1.0" - def pour_bottle? - true - end - end - assert f_true.pour_bottle? - end - - def test_pour_bottle_dsl - f_false = formula("foo") do - url "foo-1.0" - pour_bottle? do - reason "false reason" - satisfy { var == etc } - end - end - refute f_false.pour_bottle? - - f_true = formula("foo") do - url "foo-1.0" - pour_bottle? do - reason "true reason" - satisfy { true } - end - end - assert f_true.pour_bottle? - end -end - -class AliasChangeTests < Homebrew::TestCase - attr_reader :f, :new_formula, :tab, :alias_path - - def make_formula(name, version) - f = formula(name, alias_path: alias_path) { url "foo-#{version}" } - f.build = tab - f - end - - def setup - super - - alias_name = "bar" - @alias_path = "#{CoreTap.instance.alias_dir}/#{alias_name}" - - @tab = Tab.empty - - @f = make_formula("formula_name", "1.0") - @new_formula = make_formula("new_formula_name", "1.1") - - Formula.stubs(:installed).returns([f]) - end - - def test_alias_changes_when_not_installed_with_alias - tab.source["path"] = Formulary.core_path(f.name).to_s - - assert_nil f.current_installed_alias_target - assert_equal f, f.latest_formula - refute_predicate f, :installed_alias_target_changed? - refute_predicate f, :supersedes_an_installed_formula? - refute_predicate f, :alias_changed? - assert_predicate f.old_installed_formulae, :empty? - end - - def test_alias_changes_when_not_changed - tab.source["path"] = alias_path - stub_formula_loader(f, alias_path) - - assert_equal f, f.current_installed_alias_target - assert_equal f, f.latest_formula - refute_predicate f, :installed_alias_target_changed? - refute_predicate f, :supersedes_an_installed_formula? - refute_predicate f, :alias_changed? - assert_predicate f.old_installed_formulae, :empty? - end - - def test_alias_changes_when_new_alias_target - tab.source["path"] = alias_path - stub_formula_loader(new_formula, alias_path) - - assert_equal new_formula, f.current_installed_alias_target - assert_equal new_formula, f.latest_formula - assert_predicate f, :installed_alias_target_changed? - refute_predicate f, :supersedes_an_installed_formula? - assert_predicate f, :alias_changed? - assert_predicate f.old_installed_formulae, :empty? - end - - def test_alias_changes_when_old_formulae_installed - tab.source["path"] = alias_path - stub_formula_loader(new_formula, alias_path) - - assert_equal new_formula, new_formula.current_installed_alias_target - assert_equal new_formula, new_formula.latest_formula - refute_predicate new_formula, :installed_alias_target_changed? - assert_predicate new_formula, :supersedes_an_installed_formula? - assert_predicate new_formula, :alias_changed? - assert_equal [f], new_formula.old_installed_formulae - end -end - -class OutdatedVersionsTests < Homebrew::TestCase - attr_reader :outdated_prefix, - :same_prefix, - :greater_prefix, - :head_prefix, - :old_alias_target_prefix - attr_reader :f, :old_formula, :new_formula - - def setup - super - - @f = formula do - url "foo" - version "1.20" - end - - @old_formula = formula("foo@1") { url "foo-1.0" } - @new_formula = formula("foo@2") { url "foo-2.0" } - - @outdated_prefix = HOMEBREW_CELLAR/"#{f.name}/1.11" - @same_prefix = HOMEBREW_CELLAR/"#{f.name}/1.20" - @greater_prefix = HOMEBREW_CELLAR/"#{f.name}/1.21" - @head_prefix = HOMEBREW_CELLAR/"#{f.name}/HEAD" - @old_alias_target_prefix = HOMEBREW_CELLAR/"#{old_formula.name}/1.0" - end - - def alias_path - "#{@f.tap.alias_dir}/bar" - end - - def setup_tab_for_prefix(prefix, options = {}) - prefix.mkpath - tab = Tab.empty - tab.tabfile = prefix.join("INSTALL_RECEIPT.json") - 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 - - def reset_outdated_kegs - f.instance_variable_set(:@outdated_kegs, nil) - end - - def test_greater_different_tap_installed - setup_tab_for_prefix(greater_prefix, tap: "user/repo") - assert_predicate f.outdated_kegs, :empty? - end - - def test_greater_same_tap_installed - f.instance_variable_set(:@tap, CoreTap.instance) - setup_tab_for_prefix(greater_prefix, tap: "homebrew/core") - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_different_tap_installed - setup_tab_for_prefix(outdated_prefix, tap: "user/repo") - refute_predicate f.outdated_kegs, :empty? - end - - def test_outdated_same_tap_installed - f.instance_variable_set(:@tap, CoreTap.instance) - setup_tab_for_prefix(outdated_prefix, tap: "homebrew/core") - refute_predicate f.outdated_kegs, :empty? - end - - def test_outdated_follow_alias_and_alias_unchanged - f.follow_installed_alias = true - f.build = setup_tab_for_prefix(same_prefix, path: alias_path) - stub_formula_loader(f, alias_path) - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_follow_alias_and_alias_changed_and_new_target_not_installed - f.follow_installed_alias = true - f.build = setup_tab_for_prefix(same_prefix, path: alias_path) - stub_formula_loader(new_formula, alias_path) - refute_predicate f.outdated_kegs, :empty? - end - - def test_outdated_follow_alias_and_alias_changed_and_new_target_installed - f.follow_installed_alias = true - f.build = setup_tab_for_prefix(same_prefix, path: alias_path) - stub_formula_loader(new_formula, alias_path) - setup_tab_for_prefix(new_formula.prefix) # install new_formula - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_no_follow_alias_and_alias_unchanged - f.follow_installed_alias = false - f.build = setup_tab_for_prefix(same_prefix, path: alias_path) - stub_formula_loader(f, alias_path) - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_no_follow_alias_and_alias_changed - f.follow_installed_alias = false - f.build = setup_tab_for_prefix(same_prefix, path: alias_path) - stub_formula_loader(formula("foo@2") { url "foo-2.0" }, alias_path) - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_old_alias_targets_installed - @f = formula(alias_path: alias_path) { url "foo-1.0" } - tab = setup_tab_for_prefix(old_alias_target_prefix, path: alias_path) - old_formula.build = tab - Formula.stubs(:installed).returns([old_formula]) - refute_predicate f.outdated_kegs, :empty? - end - - def test_outdated_old_alias_targets_not_installed - @f = formula(alias_path: alias_path) { url "foo-1.0" } - tab = setup_tab_for_prefix(old_alias_target_prefix, path: old_formula.path) - old_formula.build = tab - Formula.stubs(:installed).returns([old_formula]) - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_same_head_installed - f.instance_variable_set(:@tap, CoreTap.instance) - setup_tab_for_prefix(head_prefix, tap: "homebrew/core") - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_different_head_installed - f.instance_variable_set(:@tap, CoreTap.instance) - setup_tab_for_prefix(head_prefix, tap: "user/repo") - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_mixed_taps_greater_version_installed - 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") - - assert_predicate f.outdated_kegs, :empty? - - setup_tab_for_prefix(greater_prefix, tap: "homebrew/core") - reset_outdated_kegs - - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_mixed_taps_outdated_version_installed - 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") - reset_outdated_kegs - - refute_predicate f.outdated_kegs, :empty? - - setup_tab_for_prefix(outdated_prefix, tap: "user/repo") - reset_outdated_kegs - - refute_predicate f.outdated_kegs, :empty? - end - - def test_outdated_same_version_tap_installed - f.instance_variable_set(:@tap, CoreTap.instance) - setup_tab_for_prefix(same_prefix, tap: "homebrew/core") - - assert_predicate f.outdated_kegs, :empty? - - setup_tab_for_prefix(same_prefix, tap: "user/repo") - reset_outdated_kegs - - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_installed_head_less_than_stable - tab = setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0" }) - refute_predicate f.outdated_kegs, :empty? - - # Tab.for_keg(head_prefix) will be fetched from CACHE but we write it anyway - tab.source["versions"] = { "stable" => f.version.to_s } - tab.write - reset_outdated_kegs - - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_fetch_head - outdated_stable_prefix = HOMEBREW_CELLAR.join("testball/1.0") - head_prefix_a = HOMEBREW_CELLAR.join("testball/HEAD") - head_prefix_b = HOMEBREW_CELLAR.join("testball/HEAD-aaaaaaa_1") - head_prefix_c = HOMEBREW_CELLAR.join("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 = HOMEBREW_PREFIX.join("testball_repo") - testball_repo.mkdir - - @f = formula("testball") do - url "foo" - version "2.10" - head "file://#{testball_repo}", using: :git - end - - testball_repo.cd do - FileUtils.touch "LICENSE" - shutup do - system "git", "init" - system "git", "add", "--all" - system "git", "commit", "-m", "Initial commit" - end - end - - refute_predicate f.outdated_kegs(fetch_head: true), :empty? - - tab_a.source["versions"] = { "stable" => f.version.to_s } - tab_a.write - reset_outdated_kegs - refute_predicate f.outdated_kegs(fetch_head: true), :empty? - - head_prefix_a.rmtree - reset_outdated_kegs - refute_predicate f.outdated_kegs(fetch_head: true), :empty? - - setup_tab_for_prefix(head_prefix_c, source_modified_time: 1) - reset_outdated_kegs - assert_predicate f.outdated_kegs(fetch_head: true), :empty? - ensure - testball_repo.rmtree if testball_repo.exist? - end - - def test_outdated_kegs_version_scheme_changed - @f = formula("testball") do - url "foo" - version "20141010" - version_scheme 1 - end - - prefix = HOMEBREW_CELLAR.join("testball/0.1") - setup_tab_for_prefix(prefix, versions: { "stable" => "0.1" }) - - refute_predicate f.outdated_kegs, :empty? - end - - def test_outdated_kegs_mixed_version_schemes - @f = formula("testball") do - url "foo" - version "20141010" - version_scheme 3 - end - - prefix_a = HOMEBREW_CELLAR.join("testball/20141009") - setup_tab_for_prefix(prefix_a, versions: { "stable" => "20141009", "version_scheme" => 1 }) - - prefix_b = HOMEBREW_CELLAR.join("testball/2.14") - setup_tab_for_prefix(prefix_b, versions: { "stable" => "2.14", "version_scheme" => 2 }) - - refute_predicate f.outdated_kegs, :empty? - reset_outdated_kegs - - prefix_c = HOMEBREW_CELLAR.join("testball/20141009") - setup_tab_for_prefix(prefix_c, versions: { "stable" => "20141009", "version_scheme" => 3 }) - - refute_predicate f.outdated_kegs, :empty? - reset_outdated_kegs - - prefix_d = HOMEBREW_CELLAR.join("testball/20141011") - setup_tab_for_prefix(prefix_d, versions: { "stable" => "20141009", "version_scheme" => 3 }) - assert_predicate f.outdated_kegs, :empty? - end - - def test_outdated_kegs_head_with_version_scheme - @f = formula("testball") do - url "foo" - version "1.0" - version_scheme 2 - end - - head_prefix = HOMEBREW_CELLAR.join("testball/HEAD") - - setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0", "version_scheme" => 1 }) - refute_predicate f.outdated_kegs, :empty? - - reset_outdated_kegs - head_prefix.rmtree - - setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0", "version_scheme" => 2 }) - assert_predicate f.outdated_kegs, :empty? - end -end diff --git a/Library/Homebrew/test/formulary_spec.rb b/Library/Homebrew/test/formulary_spec.rb index 8e9d235896..213ad511d7 100644 --- a/Library/Homebrew/test/formulary_spec.rb +++ b/Library/Homebrew/test/formulary_spec.rb @@ -216,4 +216,12 @@ describe Formulary do end end end + + describe "::core_path" do + it "returns the path to a Formula in the core tap" do + name = "foo-bar" + expect(subject.core_path(name)) + .to eq(Pathname.new("#{HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core/Formula/#{name}.rb")) + end + end end