brew/Library/Homebrew/test/keg_test.rb
Alyssa Ross 6861451f87 tests: remove more per-test file system cleanup
These locations are now all automatically cleaned up after every test.
2017-01-22 11:15:18 +00:00

476 lines
12 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
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