require "testing_env" require "keg" require "stringio" class LinkTests < 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 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 < LinkTests 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) dependencies [] alter_tab { |t| t.source["path"] = nil } assert_empty @keg.installed_dependents assert_nil 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