Convert Keg test to spec.
This commit is contained in:
parent
2401de493d
commit
af793f44c9
486
Library/Homebrew/test/keg_spec.rb
Normal file
486
Library/Homebrew/test/keg_spec.rb
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
require "keg"
|
||||||
|
require "stringio"
|
||||||
|
|
||||||
|
describe Keg do
|
||||||
|
include FileUtils
|
||||||
|
|
||||||
|
def setup_test_keg(name, version)
|
||||||
|
path = HOMEBREW_CELLAR/name/version
|
||||||
|
(path/"bin").mkpath
|
||||||
|
|
||||||
|
%w[hiworld helloworld goodbye_cruel_world].each do |file|
|
||||||
|
touch path/"bin"/file
|
||||||
|
end
|
||||||
|
|
||||||
|
keg = described_class.new(path)
|
||||||
|
kegs << keg
|
||||||
|
keg
|
||||||
|
end
|
||||||
|
|
||||||
|
around(:each) do |example|
|
||||||
|
begin
|
||||||
|
@old_stdout = $stdout
|
||||||
|
$stdout = StringIO.new
|
||||||
|
|
||||||
|
example.run
|
||||||
|
ensure
|
||||||
|
$stdout = @old_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:dst) { HOMEBREW_PREFIX/"bin"/"helloworld" }
|
||||||
|
let(:nonexistent) { Pathname.new("/some/nonexistent/path") }
|
||||||
|
let(:mode) { OpenStruct.new }
|
||||||
|
let!(:keg) { setup_test_keg("foo", "1.0") }
|
||||||
|
let(:kegs) { [] }
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
(HOMEBREW_PREFIX/"bin").mkpath
|
||||||
|
(HOMEBREW_PREFIX/"lib").mkpath
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:each) do
|
||||||
|
kegs.each(&:unlink)
|
||||||
|
rmtree HOMEBREW_PREFIX/"lib"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "::all" do
|
||||||
|
Formula.clear_racks_cache
|
||||||
|
expect(described_class.all).to eq([keg])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "#empty_installation?" do
|
||||||
|
%w[.DS_Store INSTALL_RECEIPT.json LICENSE.txt].each do |file|
|
||||||
|
touch keg/file
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(keg).to exist
|
||||||
|
expect(keg).to be_a_directory
|
||||||
|
expect(keg).not_to be_an_empty_installation
|
||||||
|
|
||||||
|
(keg/"bin").rmtree
|
||||||
|
expect(keg).to be_an_empty_installation
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "#oldname_opt_record" do
|
||||||
|
expect(keg.oldname_opt_record).to be nil
|
||||||
|
oldname_opt_record = HOMEBREW_PREFIX/"opt/oldfoo"
|
||||||
|
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/1.0")
|
||||||
|
expect(keg.oldname_opt_record).to eq(oldname_opt_record)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "#remove_oldname_opt_record" do
|
||||||
|
oldname_opt_record = HOMEBREW_PREFIX/"opt/oldfoo"
|
||||||
|
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/2.0")
|
||||||
|
keg.remove_oldname_opt_record
|
||||||
|
expect(oldname_opt_record).to be_a_symlink
|
||||||
|
oldname_opt_record.unlink
|
||||||
|
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/1.0")
|
||||||
|
keg.remove_oldname_opt_record
|
||||||
|
expect(oldname_opt_record).not_to be_a_symlink
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#link" do
|
||||||
|
it "links a Keg" do
|
||||||
|
expect(keg.link).to eq(3)
|
||||||
|
(HOMEBREW_PREFIX/"bin").children.each do |c|
|
||||||
|
expect(c.readlink).to be_relative
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with dry run set to true" do
|
||||||
|
it "only prints what would be done" do
|
||||||
|
mode.dry_run = true
|
||||||
|
|
||||||
|
expect(keg.link(mode)).to eq(0)
|
||||||
|
expect(keg).not_to be_linked
|
||||||
|
|
||||||
|
["hiworld", "helloworld", "goodbye_cruel_world"].each do |file|
|
||||||
|
expect($stdout.string).to match("#{HOMEBREW_PREFIX}/bin/#{file}")
|
||||||
|
end
|
||||||
|
expect($stdout.string.lines.count).to eq(3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "fails when already linked" do
|
||||||
|
keg.link
|
||||||
|
|
||||||
|
expect { keg.link }.to raise_error(Keg::AlreadyLinkedError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "fails when files exist" do
|
||||||
|
touch dst
|
||||||
|
|
||||||
|
expect { keg.link }.to raise_error(Keg::ConflictError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ignores broken symlinks at target" do
|
||||||
|
src = keg/"bin"/"helloworld"
|
||||||
|
dst.make_symlink(nonexistent)
|
||||||
|
keg.link
|
||||||
|
expect(dst.readlink).to eq(src.relative_path_from(dst.dirname))
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with overwrite set to true" do
|
||||||
|
it "overwrite existing files" do
|
||||||
|
touch dst
|
||||||
|
mode.overwrite = true
|
||||||
|
expect(keg.link(mode)).to eq(3)
|
||||||
|
expect(keg).to be_linked
|
||||||
|
end
|
||||||
|
|
||||||
|
it "overwrites broken symlinks" do
|
||||||
|
dst.make_symlink "nowhere"
|
||||||
|
mode.overwrite = true
|
||||||
|
expect(keg.link(mode)).to eq(3)
|
||||||
|
expect(keg).to be_linked
|
||||||
|
end
|
||||||
|
|
||||||
|
it "still supports dryrun" do
|
||||||
|
touch dst
|
||||||
|
mode.overwrite = true
|
||||||
|
mode.dry_run = true
|
||||||
|
|
||||||
|
expect(keg.link(mode)).to eq(0)
|
||||||
|
expect(keg).not_to be_linked
|
||||||
|
|
||||||
|
expect($stdout.string).to eq("#{dst}\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "also creates an opt link" do
|
||||||
|
expect(keg).not_to be_optlinked
|
||||||
|
keg.link
|
||||||
|
expect(keg).to be_optlinked
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "pkgconfig directory is created" do
|
||||||
|
link = HOMEBREW_PREFIX/"lib"/"pkgconfig"
|
||||||
|
(keg/"lib"/"pkgconfig").mkpath
|
||||||
|
keg.link
|
||||||
|
expect(link.lstat).to be_a_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "cmake directory is created" do
|
||||||
|
link = HOMEBREW_PREFIX/"lib"/"cmake"
|
||||||
|
(keg/"lib"/"cmake").mkpath
|
||||||
|
keg.link
|
||||||
|
expect(link.lstat).to be_a_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "symlinks are linked directly" do
|
||||||
|
link = HOMEBREW_PREFIX/"lib"/"pkgconfig"
|
||||||
|
|
||||||
|
(keg/"lib"/"example").mkpath
|
||||||
|
(keg/"lib"/"pkgconfig").make_symlink "example"
|
||||||
|
keg.link
|
||||||
|
|
||||||
|
expect(link.resolved_path).to be_a_symlink
|
||||||
|
expect(link.lstat).to be_a_symlink
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#unlink" do
|
||||||
|
it "unlinks a Keg" do
|
||||||
|
keg.link
|
||||||
|
expect(dst).to be_a_symlink
|
||||||
|
expect(keg.unlink).to eq(3)
|
||||||
|
expect(dst).not_to be_a_symlink
|
||||||
|
end
|
||||||
|
|
||||||
|
it "prunes empty top-level directories" do
|
||||||
|
mkpath HOMEBREW_PREFIX/"lib/foo/bar"
|
||||||
|
mkpath keg/"lib/foo/bar"
|
||||||
|
touch keg/"lib/foo/bar/file1"
|
||||||
|
|
||||||
|
keg.unlink
|
||||||
|
|
||||||
|
expect(HOMEBREW_PREFIX/"lib/foo").not_to be_a_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ignores .DS_Store when pruning empty directories" do
|
||||||
|
mkpath HOMEBREW_PREFIX/"lib/foo/bar"
|
||||||
|
touch HOMEBREW_PREFIX/"lib/foo/.DS_Store"
|
||||||
|
mkpath keg/"lib/foo/bar"
|
||||||
|
touch keg/"lib/foo/bar/file1"
|
||||||
|
|
||||||
|
keg.unlink
|
||||||
|
|
||||||
|
expect(HOMEBREW_PREFIX/"lib/foo").not_to be_a_directory
|
||||||
|
expect(HOMEBREW_PREFIX/"lib/foo/.DS_Store").not_to exist
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't remove opt link" do
|
||||||
|
keg.link
|
||||||
|
keg.unlink
|
||||||
|
expect(keg).to be_optlinked
|
||||||
|
end
|
||||||
|
|
||||||
|
it "preverves broken symlinks pointing outside the Keg" do
|
||||||
|
keg.link
|
||||||
|
dst.delete
|
||||||
|
dst.make_symlink(nonexistent)
|
||||||
|
keg.unlink
|
||||||
|
expect(dst).to be_a_symlink
|
||||||
|
end
|
||||||
|
|
||||||
|
it "preverves broken symlinks pointing into the Keg" do
|
||||||
|
keg.link
|
||||||
|
dst.resolved_path.delete
|
||||||
|
keg.unlink
|
||||||
|
expect(dst).to be_a_symlink
|
||||||
|
end
|
||||||
|
|
||||||
|
it "preverves symlinks pointing outside the Keg" do
|
||||||
|
keg.link
|
||||||
|
dst.delete
|
||||||
|
dst.make_symlink(Pathname.new("/bin/sh"))
|
||||||
|
keg.unlink
|
||||||
|
expect(dst).to be_a_symlink
|
||||||
|
end
|
||||||
|
|
||||||
|
it "preserves real files" do
|
||||||
|
keg.link
|
||||||
|
dst.delete
|
||||||
|
touch dst
|
||||||
|
keg.unlink
|
||||||
|
expect(dst).to be_a_file
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ignores nonexistent file" do
|
||||||
|
keg.link
|
||||||
|
dst.delete
|
||||||
|
expect(keg.unlink).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't remove links to symlinks" do
|
||||||
|
a = HOMEBREW_CELLAR/"a"/"1.0"
|
||||||
|
b = HOMEBREW_CELLAR/"b"/"1.0"
|
||||||
|
|
||||||
|
(a/"lib"/"example").mkpath
|
||||||
|
(a/"lib"/"example2").make_symlink "example"
|
||||||
|
(b/"lib"/"example2").mkpath
|
||||||
|
|
||||||
|
a = described_class.new(a)
|
||||||
|
b = described_class.new(b)
|
||||||
|
a.link
|
||||||
|
|
||||||
|
lib = HOMEBREW_PREFIX/"lib"
|
||||||
|
expect(lib.children.length).to eq(2)
|
||||||
|
expect { b.link }.to raise_error(Keg::ConflictError)
|
||||||
|
expect(lib.children.length).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "removes broken symlinks that conflict with directories" do
|
||||||
|
a = HOMEBREW_CELLAR/"a"/"1.0"
|
||||||
|
(a/"lib"/"foo").mkpath
|
||||||
|
|
||||||
|
keg = described_class.new(a)
|
||||||
|
|
||||||
|
link = HOMEBREW_PREFIX/"lib"/"foo"
|
||||||
|
link.parent.mkpath
|
||||||
|
link.make_symlink(nonexistent)
|
||||||
|
|
||||||
|
keg.link
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#optlink" do
|
||||||
|
it "creates an opt link" do
|
||||||
|
oldname_opt_record = HOMEBREW_PREFIX/"opt/oldfoo"
|
||||||
|
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/1.0")
|
||||||
|
keg_record = HOMEBREW_CELLAR/"foo"/"2.0"
|
||||||
|
(keg_record/"bin").mkpath
|
||||||
|
keg = described_class.new(keg_record)
|
||||||
|
keg.optlink
|
||||||
|
expect(keg_record).to eq(oldname_opt_record.resolved_path)
|
||||||
|
keg.uninstall
|
||||||
|
expect(oldname_opt_record).not_to be_a_symlink
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't fail if already opt-linked" do
|
||||||
|
keg.opt_record.make_relative_symlink Pathname.new(keg)
|
||||||
|
keg.optlink
|
||||||
|
expect(keg).to be_optlinked
|
||||||
|
end
|
||||||
|
|
||||||
|
it "replaces an existing directory" do
|
||||||
|
keg.opt_record.mkpath
|
||||||
|
keg.optlink
|
||||||
|
expect(keg).to be_optlinked
|
||||||
|
end
|
||||||
|
|
||||||
|
it "replaces an existing file" do
|
||||||
|
keg.opt_record.parent.mkpath
|
||||||
|
keg.opt_record.write("foo")
|
||||||
|
keg.optlink
|
||||||
|
expect(keg).to be_optlinked
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "#link and #unlink" do
|
||||||
|
expect(keg).not_to be_linked
|
||||||
|
keg.link
|
||||||
|
expect(keg).to be_linked
|
||||||
|
keg.unlink
|
||||||
|
expect(keg).not_to be_linked
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::find_some_installed_dependents" do
|
||||||
|
def stub_formula_name(name)
|
||||||
|
f = formula(name) { url "foo-1.0" }
|
||||||
|
stub_formula_loader f
|
||||||
|
stub_formula_loader f, "homebrew/core/#{f}"
|
||||||
|
f
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup_test_keg(name, version)
|
||||||
|
f = stub_formula_name(name)
|
||||||
|
keg = super
|
||||||
|
Tab.create(f, DevelopmentTools.default_compiler, :libcxx).write
|
||||||
|
keg
|
||||||
|
end
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
keg.link
|
||||||
|
end
|
||||||
|
|
||||||
|
def alter_tab(keg = dependent)
|
||||||
|
tab = Tab.for_keg(keg)
|
||||||
|
yield tab
|
||||||
|
tab.write
|
||||||
|
end
|
||||||
|
|
||||||
|
# 1.1.6 is the earliest version of Homebrew that generates correct runtime
|
||||||
|
# dependency lists in Tabs.
|
||||||
|
def dependencies(deps, homebrew_version: "1.1.6")
|
||||||
|
alter_tab do |tab|
|
||||||
|
tab.homebrew_version = homebrew_version
|
||||||
|
tab.tabfile = dependent/Tab::FILENAME
|
||||||
|
tab.runtime_dependencies = deps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unreliable_dependencies(deps)
|
||||||
|
# 1.1.5 is (hopefully!) the last version of Homebrew that generates
|
||||||
|
# incorrect runtime dependency lists in Tabs.
|
||||||
|
dependencies(deps, homebrew_version: "1.1.5")
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:dependent) { setup_test_keg("bar", "1.0") }
|
||||||
|
|
||||||
|
# Test with a keg whose formula isn't known.
|
||||||
|
# This can happen if e.g. a formula is installed
|
||||||
|
# from a file path or URL.
|
||||||
|
specify "unknown Formula" do
|
||||||
|
allow(Formulary).to receive(:loader_for).and_call_original
|
||||||
|
alter_tab(keg) do |t|
|
||||||
|
t.source["tap"] = "some/tap"
|
||||||
|
t.source["path"] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
dependencies [{ "full_name" => "some/tap/foo", "version" => "1.0" }]
|
||||||
|
expect(keg.installed_dependents).to eq([dependent])
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar 1.0"]])
|
||||||
|
|
||||||
|
dependencies nil
|
||||||
|
# It doesn't make sense for a keg with no formula to have any dependents,
|
||||||
|
# so that can't really be tested.
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to be nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "a dependency with no Tap in Tab" do
|
||||||
|
tap_dep = setup_test_keg("baz", "1.0")
|
||||||
|
|
||||||
|
alter_tab(keg) { |t| t.source["tap"] = nil }
|
||||||
|
|
||||||
|
dependencies nil
|
||||||
|
Formula["bar"].class.depends_on "foo"
|
||||||
|
Formula["bar"].class.depends_on "baz"
|
||||||
|
|
||||||
|
result = described_class.find_some_installed_dependents([keg, tap_dep])
|
||||||
|
expect(result).to eq([[keg, tap_dep], ["bar"]])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "no dependencies anywhere" do
|
||||||
|
dependencies nil
|
||||||
|
expect(keg.installed_dependents).to be_empty
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to be nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "missing Formula dependency" do
|
||||||
|
dependencies nil
|
||||||
|
Formula["bar"].class.depends_on "foo"
|
||||||
|
expect(keg.installed_dependents).to be_empty
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar"]])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "uninstalling dependent and dependency" do
|
||||||
|
dependencies nil
|
||||||
|
Formula["bar"].class.depends_on "foo"
|
||||||
|
expect(keg.installed_dependents).to be_empty
|
||||||
|
expect(described_class.find_some_installed_dependents([keg, dependent])).to be nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "renamed dependency" do
|
||||||
|
dependencies nil
|
||||||
|
|
||||||
|
stub_formula_loader Formula["foo"], "homebrew/core/foo-old"
|
||||||
|
renamed_path = HOMEBREW_CELLAR/"foo-old"
|
||||||
|
(HOMEBREW_CELLAR/"foo").rename(renamed_path)
|
||||||
|
renamed_keg = described_class.new(renamed_path/"1.0")
|
||||||
|
|
||||||
|
Formula["bar"].class.depends_on "foo"
|
||||||
|
|
||||||
|
result = described_class.find_some_installed_dependents([renamed_keg])
|
||||||
|
expect(result).to eq([[renamed_keg], ["bar"]])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "empty dependencies in Tab" do
|
||||||
|
dependencies []
|
||||||
|
expect(keg.installed_dependents).to be_empty
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to be nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "same name but different version in Tab" do
|
||||||
|
dependencies [{ "full_name" => "foo", "version" => "1.1" }]
|
||||||
|
expect(keg.installed_dependents).to eq([dependent])
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar 1.0"]])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "different name and same version in Tab" do
|
||||||
|
stub_formula_name("baz")
|
||||||
|
dependencies [{ "full_name" => "baz", "version" => keg.version.to_s }]
|
||||||
|
expect(keg.installed_dependents).to be_empty
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to be nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "same name and version in Tab" do
|
||||||
|
dependencies [{ "full_name" => "foo", "version" => "1.0" }]
|
||||||
|
expect(keg.installed_dependents).to eq([dependent])
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar 1.0"]])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "fallback for old versions" do
|
||||||
|
unreliable_dependencies [{ "full_name" => "baz", "version" => "1.0" }]
|
||||||
|
Formula["bar"].class.depends_on "foo"
|
||||||
|
expect(keg.installed_dependents).to be_empty
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar"]])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "non-opt-linked" do
|
||||||
|
keg.remove_opt_record
|
||||||
|
dependencies [{ "full_name" => "foo", "version" => "1.0" }]
|
||||||
|
expect(keg.installed_dependents).to be_empty
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to be nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "keg-only" do
|
||||||
|
keg.unlink
|
||||||
|
Formula["foo"].class.keg_only "a good reason"
|
||||||
|
dependencies [{ "full_name" => "foo", "version" => "1.1" }] # different version
|
||||||
|
expect(keg.installed_dependents).to eq([dependent])
|
||||||
|
expect(described_class.find_some_installed_dependents([keg])).to eq([[keg], ["bar 1.0"]])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,475 +0,0 @@
|
|||||||
require "testing_env"
|
|
||||||
require "keg"
|
|
||||||
require "stringio"
|
|
||||||
|
|
||||||
class LinkTestCase < Homebrew::TestCase
|
|
||||||
include FileUtils
|
|
||||||
|
|
||||||
def setup_test_keg(name, version)
|
|
||||||
path = HOMEBREW_CELLAR.join(name, version)
|
|
||||||
path.join("bin").mkpath
|
|
||||||
|
|
||||||
%w[hiworld helloworld goodbye_cruel_world].each do |file|
|
|
||||||
touch path.join("bin", file)
|
|
||||||
end
|
|
||||||
|
|
||||||
keg = Keg.new(path)
|
|
||||||
@kegs ||= []
|
|
||||||
@kegs << keg
|
|
||||||
keg
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup
|
|
||||||
super
|
|
||||||
|
|
||||||
@keg = setup_test_keg("foo", "1.0")
|
|
||||||
@dst = HOMEBREW_PREFIX.join("bin", "helloworld")
|
|
||||||
@nonexistent = Pathname.new("/some/nonexistent/path")
|
|
||||||
|
|
||||||
@mode = OpenStruct.new
|
|
||||||
|
|
||||||
@old_stdout = $stdout
|
|
||||||
$stdout = StringIO.new
|
|
||||||
|
|
||||||
mkpath HOMEBREW_PREFIX/"bin"
|
|
||||||
mkpath HOMEBREW_PREFIX/"lib"
|
|
||||||
end
|
|
||||||
|
|
||||||
def teardown
|
|
||||||
@kegs.each(&:unlink)
|
|
||||||
$stdout = @old_stdout
|
|
||||||
rmtree HOMEBREW_PREFIX/"lib"
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class LinkTests < LinkTestCase
|
|
||||||
def test_all
|
|
||||||
Formula.clear_racks_cache
|
|
||||||
assert_equal [@keg], Keg.all
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_empty_installation
|
|
||||||
%w[.DS_Store INSTALL_RECEIPT.json LICENSE.txt].each do |file|
|
|
||||||
touch @keg/file
|
|
||||||
end
|
|
||||||
assert_predicate @keg, :exist?
|
|
||||||
assert_predicate @keg, :directory?
|
|
||||||
refute_predicate @keg, :empty_installation?
|
|
||||||
|
|
||||||
(@keg/"bin").rmtree
|
|
||||||
assert_predicate @keg, :empty_installation?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_linking_keg
|
|
||||||
assert_equal 3, @keg.link
|
|
||||||
(HOMEBREW_PREFIX/"bin").children.each { |c| assert_predicate c.readlink, :relative? }
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlinking_keg
|
|
||||||
@keg.link
|
|
||||||
assert_predicate @dst, :symlink?
|
|
||||||
assert_equal 3, @keg.unlink
|
|
||||||
refute_predicate @dst, :symlink?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_oldname_opt_record
|
|
||||||
assert_nil @keg.oldname_opt_record
|
|
||||||
oldname_opt_record = HOMEBREW_PREFIX/"opt/oldfoo"
|
|
||||||
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/1.0")
|
|
||||||
assert_equal oldname_opt_record, @keg.oldname_opt_record
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_optlink_relink
|
|
||||||
oldname_opt_record = HOMEBREW_PREFIX/"opt/oldfoo"
|
|
||||||
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/1.0")
|
|
||||||
keg_record = HOMEBREW_CELLAR.join("foo", "2.0")
|
|
||||||
keg_record.join("bin").mkpath
|
|
||||||
keg = Keg.new(keg_record)
|
|
||||||
keg.optlink
|
|
||||||
assert_equal keg_record, oldname_opt_record.resolved_path
|
|
||||||
keg.uninstall
|
|
||||||
refute_predicate oldname_opt_record, :symlink?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_remove_oldname_opt_record
|
|
||||||
oldname_opt_record = HOMEBREW_PREFIX/"opt/oldfoo"
|
|
||||||
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/2.0")
|
|
||||||
@keg.remove_oldname_opt_record
|
|
||||||
assert_predicate oldname_opt_record, :symlink?
|
|
||||||
oldname_opt_record.unlink
|
|
||||||
oldname_opt_record.make_relative_symlink(HOMEBREW_CELLAR/"foo/1.0")
|
|
||||||
@keg.remove_oldname_opt_record
|
|
||||||
refute_predicate oldname_opt_record, :symlink?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_link_dry_run
|
|
||||||
@mode.dry_run = true
|
|
||||||
|
|
||||||
assert_equal 0, @keg.link(@mode)
|
|
||||||
refute_predicate @keg, :linked?
|
|
||||||
|
|
||||||
["hiworld", "helloworld", "goodbye_cruel_world"].each do |file|
|
|
||||||
assert_match "#{HOMEBREW_PREFIX}/bin/#{file}", $stdout.string
|
|
||||||
end
|
|
||||||
assert_equal 3, $stdout.string.lines.count
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_linking_fails_when_already_linked
|
|
||||||
@keg.link
|
|
||||||
assert_raises(Keg::AlreadyLinkedError) { @keg.link }
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_linking_fails_when_files_exist
|
|
||||||
touch @dst
|
|
||||||
assert_raises(Keg::ConflictError) { @keg.link }
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_link_ignores_broken_symlinks_at_target
|
|
||||||
src = @keg.join("bin", "helloworld")
|
|
||||||
@dst.make_symlink(@nonexistent)
|
|
||||||
@keg.link
|
|
||||||
assert_equal src.relative_path_from(@dst.dirname), @dst.readlink
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_link_overwrite
|
|
||||||
touch @dst
|
|
||||||
@mode.overwrite = true
|
|
||||||
assert_equal 3, @keg.link(@mode)
|
|
||||||
assert_predicate @keg, :linked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_link_overwrite_broken_symlinks
|
|
||||||
@dst.make_symlink "nowhere"
|
|
||||||
@mode.overwrite = true
|
|
||||||
assert_equal 3, @keg.link(@mode)
|
|
||||||
assert_predicate @keg, :linked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_link_overwrite_dryrun
|
|
||||||
touch @dst
|
|
||||||
@mode.overwrite = true
|
|
||||||
@mode.dry_run = true
|
|
||||||
|
|
||||||
assert_equal 0, @keg.link(@mode)
|
|
||||||
refute_predicate @keg, :linked?
|
|
||||||
|
|
||||||
assert_equal "#{@dst}\n", $stdout.string
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlink_prunes_empty_toplevel_directories
|
|
||||||
mkpath HOMEBREW_PREFIX/"lib/foo/bar"
|
|
||||||
mkpath @keg/"lib/foo/bar"
|
|
||||||
touch @keg/"lib/foo/bar/file1"
|
|
||||||
|
|
||||||
@keg.unlink
|
|
||||||
|
|
||||||
refute_predicate HOMEBREW_PREFIX/"lib/foo", :directory?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlink_ignores_ds_store_when_pruning_empty_dirs
|
|
||||||
mkpath HOMEBREW_PREFIX/"lib/foo/bar"
|
|
||||||
touch HOMEBREW_PREFIX/"lib/foo/.DS_Store"
|
|
||||||
mkpath @keg/"lib/foo/bar"
|
|
||||||
touch @keg/"lib/foo/bar/file1"
|
|
||||||
|
|
||||||
@keg.unlink
|
|
||||||
|
|
||||||
refute_predicate HOMEBREW_PREFIX/"lib/foo", :directory?
|
|
||||||
refute_predicate HOMEBREW_PREFIX/"lib/foo/.DS_Store", :exist?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_linking_creates_opt_link
|
|
||||||
refute_predicate @keg, :optlinked?
|
|
||||||
@keg.link
|
|
||||||
assert_predicate @keg, :optlinked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlinking_does_not_remove_opt_link
|
|
||||||
@keg.link
|
|
||||||
@keg.unlink
|
|
||||||
assert_predicate @keg, :optlinked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_existing_opt_link
|
|
||||||
@keg.opt_record.make_relative_symlink Pathname.new(@keg)
|
|
||||||
@keg.optlink
|
|
||||||
assert_predicate @keg, :optlinked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_existing_opt_link_directory
|
|
||||||
@keg.opt_record.mkpath
|
|
||||||
@keg.optlink
|
|
||||||
assert_predicate @keg, :optlinked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_existing_opt_link_file
|
|
||||||
@keg.opt_record.parent.mkpath
|
|
||||||
@keg.opt_record.write("foo")
|
|
||||||
@keg.optlink
|
|
||||||
assert_predicate @keg, :optlinked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_linked_keg
|
|
||||||
refute_predicate @keg, :linked?
|
|
||||||
@keg.link
|
|
||||||
assert_predicate @keg, :linked?
|
|
||||||
@keg.unlink
|
|
||||||
refute_predicate @keg, :linked?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlink_preserves_broken_symlink_pointing_outside_the_keg
|
|
||||||
@keg.link
|
|
||||||
@dst.delete
|
|
||||||
@dst.make_symlink(@nonexistent)
|
|
||||||
@keg.unlink
|
|
||||||
assert_predicate @dst, :symlink?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlink_preserves_broken_symlink_pointing_into_the_keg
|
|
||||||
@keg.link
|
|
||||||
@dst.resolved_path.delete
|
|
||||||
@keg.unlink
|
|
||||||
assert_predicate @dst, :symlink?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlink_preserves_symlink_pointing_outside_of_keg
|
|
||||||
@keg.link
|
|
||||||
@dst.delete
|
|
||||||
@dst.make_symlink(Pathname.new("/bin/sh"))
|
|
||||||
@keg.unlink
|
|
||||||
assert_predicate @dst, :symlink?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlink_preserves_real_file
|
|
||||||
@keg.link
|
|
||||||
@dst.delete
|
|
||||||
touch @dst
|
|
||||||
@keg.unlink
|
|
||||||
assert_predicate @dst, :file?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unlink_ignores_nonexistent_file
|
|
||||||
@keg.link
|
|
||||||
@dst.delete
|
|
||||||
assert_equal 2, @keg.unlink
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_pkgconfig_is_mkpathed
|
|
||||||
link = HOMEBREW_PREFIX.join("lib", "pkgconfig")
|
|
||||||
@keg.join("lib", "pkgconfig").mkpath
|
|
||||||
@keg.link
|
|
||||||
assert_predicate link.lstat, :directory?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_cmake_is_mkpathed
|
|
||||||
link = HOMEBREW_PREFIX.join("lib", "cmake")
|
|
||||||
@keg.join("lib", "cmake").mkpath
|
|
||||||
@keg.link
|
|
||||||
assert_predicate link.lstat, :directory?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_symlinks_are_linked_directly
|
|
||||||
link = HOMEBREW_PREFIX.join("lib", "pkgconfig")
|
|
||||||
|
|
||||||
@keg.join("lib", "example").mkpath
|
|
||||||
@keg.join("lib", "pkgconfig").make_symlink "example"
|
|
||||||
@keg.link
|
|
||||||
|
|
||||||
assert_predicate link.resolved_path, :symlink?
|
|
||||||
assert_predicate link.lstat, :symlink?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_links_to_symlinks_are_not_removed
|
|
||||||
a = HOMEBREW_CELLAR.join("a", "1.0")
|
|
||||||
b = HOMEBREW_CELLAR.join("b", "1.0")
|
|
||||||
|
|
||||||
a.join("lib", "example").mkpath
|
|
||||||
a.join("lib", "example2").make_symlink "example"
|
|
||||||
b.join("lib", "example2").mkpath
|
|
||||||
|
|
||||||
a = Keg.new(a)
|
|
||||||
b = Keg.new(b)
|
|
||||||
a.link
|
|
||||||
|
|
||||||
lib = HOMEBREW_PREFIX.join("lib")
|
|
||||||
assert_equal 2, lib.children.length
|
|
||||||
assert_raises(Keg::ConflictError) { b.link }
|
|
||||||
assert_equal 2, lib.children.length
|
|
||||||
ensure
|
|
||||||
a.unlink
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_removes_broken_symlinks_that_conflict_with_directories
|
|
||||||
a = HOMEBREW_CELLAR.join("a", "1.0")
|
|
||||||
a.join("lib", "foo").mkpath
|
|
||||||
|
|
||||||
keg = Keg.new(a)
|
|
||||||
|
|
||||||
link = HOMEBREW_PREFIX.join("lib", "foo")
|
|
||||||
link.parent.mkpath
|
|
||||||
link.make_symlink(@nonexistent)
|
|
||||||
|
|
||||||
keg.link
|
|
||||||
ensure
|
|
||||||
keg.unlink
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class InstalledDependantsTests < LinkTestCase
|
|
||||||
def stub_formula_name(name)
|
|
||||||
f = formula(name) { url "foo-1.0" }
|
|
||||||
stub_formula_loader f
|
|
||||||
stub_formula_loader f, "homebrew/core/#{f}"
|
|
||||||
f
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_test_keg(name, version)
|
|
||||||
f = stub_formula_name(name)
|
|
||||||
keg = super
|
|
||||||
Tab.create(f, DevelopmentTools.default_compiler, :libcxx).write
|
|
||||||
keg
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup
|
|
||||||
super
|
|
||||||
@dependent = setup_test_keg("bar", "1.0")
|
|
||||||
@keg.link
|
|
||||||
end
|
|
||||||
|
|
||||||
def alter_tab(keg = @dependent)
|
|
||||||
tab = Tab.for_keg(keg)
|
|
||||||
yield tab
|
|
||||||
tab.write
|
|
||||||
end
|
|
||||||
|
|
||||||
# 1.1.6 is the earliest version of Homebrew that generates correct runtime
|
|
||||||
# dependency lists in tabs.
|
|
||||||
def dependencies(deps, homebrew_version: "1.1.6")
|
|
||||||
alter_tab do |tab|
|
|
||||||
tab.homebrew_version = homebrew_version
|
|
||||||
tab.tabfile = @dependent.join("INSTALL_RECEIPT.json")
|
|
||||||
tab.runtime_dependencies = deps
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def unreliable_dependencies(deps)
|
|
||||||
# 1.1.5 is (hopefully!) the last version of Homebrew that generates
|
|
||||||
# incorrect runtime dependency lists in tabs.
|
|
||||||
dependencies(deps, homebrew_version: "1.1.5")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test with a keg whose formula isn't known.
|
|
||||||
# This can happen if e.g. a formula is installed
|
|
||||||
# from a file path or URL.
|
|
||||||
def test_unknown_formula
|
|
||||||
Formulary.unstub(:loader_for)
|
|
||||||
alter_tab(@keg) do |t|
|
|
||||||
t.source["tap"] = "some/tap"
|
|
||||||
t.source["path"] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
dependencies [{ "full_name" => "some/tap/foo", "version" => "1.0" }]
|
|
||||||
assert_equal [@dependent], @keg.installed_dependents
|
|
||||||
assert_equal [[@keg], ["bar 1.0"]], Keg.find_some_installed_dependents([@keg])
|
|
||||||
|
|
||||||
dependencies nil
|
|
||||||
# It doesn't make sense for a keg with no formula to have any dependents,
|
|
||||||
# so that can't really be tested.
|
|
||||||
assert_nil Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_a_dependency_with_no_tap_in_tab
|
|
||||||
@tap_dep = setup_test_keg("baz", "1.0")
|
|
||||||
|
|
||||||
alter_tab(@keg) { |t| t.source["tap"] = nil }
|
|
||||||
|
|
||||||
dependencies nil
|
|
||||||
Formula["bar"].class.depends_on "foo"
|
|
||||||
Formula["bar"].class.depends_on "baz"
|
|
||||||
|
|
||||||
result = Keg.find_some_installed_dependents([@keg, @tap_dep])
|
|
||||||
assert_equal [[@keg, @tap_dep], ["bar"]], result
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_no_dependencies_anywhere
|
|
||||||
dependencies nil
|
|
||||||
assert_empty @keg.installed_dependents
|
|
||||||
assert_nil Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_missing_formula_dependency
|
|
||||||
dependencies nil
|
|
||||||
Formula["bar"].class.depends_on "foo"
|
|
||||||
assert_empty @keg.installed_dependents
|
|
||||||
assert_equal [[@keg], ["bar"]], Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_uninstalling_dependent_and_dependency
|
|
||||||
dependencies nil
|
|
||||||
Formula["bar"].class.depends_on "foo"
|
|
||||||
assert_empty @keg.installed_dependents
|
|
||||||
assert_nil Keg.find_some_installed_dependents([@keg, @dependent])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_renamed_dependency
|
|
||||||
dependencies nil
|
|
||||||
|
|
||||||
stub_formula_loader Formula["foo"], "homebrew/core/foo-old"
|
|
||||||
renamed_path = HOMEBREW_CELLAR/"foo-old"
|
|
||||||
(HOMEBREW_CELLAR/"foo").rename(renamed_path)
|
|
||||||
renamed_keg = Keg.new(renamed_path.join("1.0"))
|
|
||||||
|
|
||||||
Formula["bar"].class.depends_on "foo"
|
|
||||||
|
|
||||||
result = Keg.find_some_installed_dependents([renamed_keg])
|
|
||||||
assert_equal [[renamed_keg], ["bar"]], result
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_empty_dependencies_in_tab
|
|
||||||
dependencies []
|
|
||||||
assert_empty @keg.installed_dependents
|
|
||||||
assert_nil Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_same_name_different_version_in_tab
|
|
||||||
dependencies [{ "full_name" => "foo", "version" => "1.1" }]
|
|
||||||
assert_equal [@dependent], @keg.installed_dependents
|
|
||||||
assert_equal [[@keg], ["bar 1.0"]], Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_different_name_same_version_in_tab
|
|
||||||
stub_formula_name("baz")
|
|
||||||
dependencies [{ "full_name" => "baz", "version" => @keg.version.to_s }]
|
|
||||||
assert_empty @keg.installed_dependents
|
|
||||||
assert_nil Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_same_name_and_version_in_tab
|
|
||||||
dependencies [{ "full_name" => "foo", "version" => "1.0" }]
|
|
||||||
assert_equal [@dependent], @keg.installed_dependents
|
|
||||||
assert_equal [[@keg], ["bar 1.0"]], Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fallback_for_old_versions
|
|
||||||
unreliable_dependencies [{ "full_name" => "baz", "version" => "1.0" }]
|
|
||||||
Formula["bar"].class.depends_on "foo"
|
|
||||||
assert_empty @keg.installed_dependents
|
|
||||||
assert_equal [[@keg], ["bar"]], Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_nonoptlinked
|
|
||||||
@keg.remove_opt_record
|
|
||||||
dependencies [{ "full_name" => "foo", "version" => "1.0" }]
|
|
||||||
assert_empty @keg.installed_dependents
|
|
||||||
assert_nil Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_keg_only
|
|
||||||
@keg.unlink
|
|
||||||
Formula["foo"].class.keg_only "a good reason"
|
|
||||||
dependencies [{ "full_name" => "foo", "version" => "1.1" }] # different version
|
|
||||||
assert_equal [@dependent], @keg.installed_dependents
|
|
||||||
assert_equal [[@keg], ["bar 1.0"]], Keg.find_some_installed_dependents([@keg])
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
x
Reference in New Issue
Block a user