
Previously, trying to resolve the dependencies of a keg would raise an exception if the formulae for any of the dependencies could not be found (e.g. if it had been moved to another tap). This commit updates the dependency finding logic to catch these exceptions, and fall back to comparing names and taps of formulae, which should give the correct behaviour. Fixes #1586.
408 lines
10 KiB
Ruby
408 lines
10 KiB
Ruby
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
|
|
@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 do |keg|
|
|
keg.unlink
|
|
keg.uninstall
|
|
end
|
|
|
|
$stdout = @old_stdout
|
|
|
|
rmtree HOMEBREW_PREFIX/"bin"
|
|
rmtree HOMEBREW_PREFIX/"lib"
|
|
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
|
|
a.uninstall
|
|
b.uninstall
|
|
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
|
|
keg.uninstall
|
|
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")
|
|
end
|
|
|
|
def alter_tab(keg = @dependent)
|
|
tab = Tab.for_keg(keg)
|
|
yield tab
|
|
tab.write
|
|
end
|
|
|
|
def dependencies(deps)
|
|
alter_tab do |tab|
|
|
tab.tabfile = @dependent.join("INSTALL_RECEIPT.json")
|
|
tab.runtime_dependencies = deps
|
|
end
|
|
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 { |t| t.source["tap"] = "some/tap" }
|
|
dependencies [{ "full_name" => "some/tap/bar", "version" => "1.0" }]
|
|
alter_tab { |t| t.source["path"] = nil }
|
|
assert_equal [@dependent], @keg.installed_dependents
|
|
assert_equal [[@keg], ["bar 1.0"]], Keg.find_some_installed_dependents([@keg])
|
|
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_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_empty @keg.installed_dependents
|
|
assert_nil 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
|
|
end
|