From 3d559fa79641735193636cbf6240c082e6ca171c Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Wed, 14 Sep 2016 23:18:55 +0100 Subject: [PATCH 01/12] Add Formula#installed_alias_path --- Library/Homebrew/formula.rb | 16 ++++++++++++---- Library/Homebrew/test/test_formula.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 599b493920..f2da6fbc86 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -60,7 +60,7 @@ class Formula # e.g. `this-formula` attr_reader :name - # The name specified when installing this {Formula}. + # The name that was used to identify this {Formula}. # Could be the name of the {Formula}, or an alias. # e.g. `another-name-for-this-formula` attr_reader :alias_path @@ -228,7 +228,15 @@ class Formula public - # The path that was specified to find/install this formula. + # The alias path that was used to install this formula, if present. + # Can differ from alias_path, which is the alias used to find the formula, + # and is specified to this instance. + def installed_alias_path + path = build.source["path"] if build.is_a?(Tab) + path if path =~ %r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases} + end + + # The path that was specified to find this formula. def specified_path alias_path || path end @@ -503,13 +511,13 @@ class Formula prefix.parent end - # All of current installed prefix directories. + # All currently installed prefix directories. # @private def installed_prefixes rack.directory? ? rack.subdirs : [] end - # All of current installed kegs. + # All currently installed kegs. # @private def installed_kegs installed_prefixes.map { |dir| Keg.new(dir) } diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb index 7c09e765f2..2eb64ebb67 100644 --- a/Library/Homebrew/test/test_formula.rb +++ b/Library/Homebrew/test/test_formula.rb @@ -248,6 +248,32 @@ class FormulaTests < Homebrew::TestCase 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_formula_spec_integration f = formula do homepage "http://example.com" From 2a683f2569614850f79534a8547fd96cc52c7850 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Thu, 15 Sep 2016 16:01:18 +0100 Subject: [PATCH 02/12] upgrade, outdated: follow alias changes --- Library/Homebrew/cmd/outdated.rb | 21 +++- Library/Homebrew/cmd/upgrade.rb | 28 +++-- Library/Homebrew/extend/ARGV.rb | 16 ++- Library/Homebrew/formula.rb | 138 ++++++++++++++++++++---- Library/Homebrew/formulary.rb | 39 ++++--- Library/Homebrew/test/test_formulary.rb | 2 +- 6 files changed, 188 insertions(+), 56 deletions(-) diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb index 7afa41df4a..f9739cd7a0 100644 --- a/Library/Homebrew/cmd/outdated.rb +++ b/Library/Homebrew/cmd/outdated.rb @@ -43,15 +43,26 @@ module Homebrew outdated_formulae.each do |f| if verbose - outdated_versions = f.outdated_versions(fetch_head: fetch_head) - current_version = if f.head? && outdated_versions.any? { |v| v.to_s == f.pkg_version.to_s } + outdated_kegs = f.outdated_kegs(fetch_head: fetch_head) + + current_version = if f.alias_changed? + latest = f.latest_formula + "#{latest.name} (#{latest.pkg_version})" + elsif f.head? && outdated_kegs.any? { |k| k.version.to_s == f.pkg_version.to_s } + # There is a newer HEAD but the version number has not changed. "latest HEAD" else f.pkg_version.to_s end - puts "#{f.full_name} (#{outdated_versions.join(", ")}) < #{current_version}" + + outdated_versions = outdated_kegs. + group_by(&:name). + sort_by(&:first). + map { |name, kegs| "#{name} (#{kegs.map(&:version) * ", "})" } * ", " + + puts "#{outdated_versions} < #{current_version}" else - puts f.full_name + puts f.full_installed_specified_name end end end @@ -62,7 +73,7 @@ module Homebrew outdated_formulae = formulae.select { |f| f.outdated?(fetch_head: fetch_head) } outdated = outdated_formulae.each do |f| - outdated_versions = f.outdated_versions(fetch_head: fetch_head) + outdated_versions = f.outdated_kegs(fetch_head: fetch_head).map(&:version) current_version = if f.head? && outdated_versions.any? { |v| v.to_s == f.pkg_version.to_s } "HEAD" else diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index 6968fbda88..c56a733843 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -37,10 +37,10 @@ module Homebrew (ARGV.resolved_formulae - outdated).each do |f| versions = f.installed_kegs.map(&:version) if versions.empty? - onoe "#{f.full_name} not installed" + onoe "#{f.full_specified_name} not installed" else version = versions.max - onoe "#{f.full_name} #{version} already installed" + onoe "#{f.full_specified_name} #{version} already installed" end end exit 1 if outdated.empty? @@ -51,19 +51,21 @@ module Homebrew outdated -= pinned end - if outdated.empty? + formulae_to_install = outdated.map(&:latest_formula) + + if formulae_to_install.empty? oh1 "No packages to upgrade" else - oh1 "Upgrading #{outdated.length} outdated package#{plural(outdated.length)}, with result:" - puts outdated.map { |f| "#{f.full_name} #{f.pkg_version}" } * ", " + oh1 "Upgrading #{formulae_to_install.length} outdated package#{plural(formulae_to_install.length)}, with result:" + puts formulae_to_install.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", " end unless upgrade_pinned? || pinned.empty? oh1 "Not upgrading #{pinned.length} pinned package#{plural(pinned.length)}:" - puts pinned.map { |f| "#{f.full_name} #{f.pkg_version}" } * ", " + puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", " end - outdated.each do |f| + formulae_to_install.each do |f| upgrade_formula(f) next unless ARGV.include?("--cleanup") next unless f.installed? @@ -76,7 +78,11 @@ module Homebrew end def upgrade_formula(f) - outdated_keg = Keg.new(f.linked_keg.resolved_path) if f.linked_keg.directory? + formulae_maybe_with_kegs = [f] + f.old_installed_formulae + outdated_kegs = formulae_maybe_with_kegs. + map(&:linked_keg). + select(&:directory?). + map { |k| Keg.new(k.resolved_path) } fi = FormulaInstaller.new(f) fi.options = f.build.used_options @@ -87,12 +93,12 @@ module Homebrew fi.debug = ARGV.debug? fi.prelude - oh1 "Upgrading #{f.full_name}" + oh1 "Upgrading #{f.full_specified_name}" # first we unlink the currently active keg for this formula otherwise it is # possible for the existing build to interfere with the build we are about to # do! Seriously, it happens! - outdated_keg.unlink if outdated_keg + outdated_kegs.each(&:unlink) fi.install fi.finish @@ -117,7 +123,7 @@ module Homebrew ensure # restore previous installation state if build failed begin - outdated_keg.link if outdated_keg && !f.installed? + outdated_kegs.each(&:link) if !f.installed? rescue nil end diff --git a/Library/Homebrew/extend/ARGV.rb b/Library/Homebrew/extend/ARGV.rb index 63e46a1e24..d9f5998771 100644 --- a/Library/Homebrew/extend/ARGV.rb +++ b/Library/Homebrew/extend/ARGV.rb @@ -37,11 +37,23 @@ module HomebrewArgvExtension f.version.update_commit(k.version.version.commit) if k.version.head? end end - f else rack = Formulary.to_rack(name) - Formulary.from_rack(rack, spec(nil)) + alias_path = Formulary.factory(name).alias_path + f = Formulary.from_rack(rack, spec(nil), alias_path: alias_path) end + + # If this formula was installed with an alias that has since changed, + # then it was specified explicitly in ARGV. (Using the alias would + # instead have found the new formula.) + # + # Because of this, the user is referring to this specific formula, + # not any formula targetted by the same alias, so in this context + # the formula shouldn't be considered outdated if the alias used to + # install it has changed. + f.follow_installed_alias = false + + f end end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index f2da6fbc86..41ddda3076 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -60,16 +60,24 @@ class Formula # e.g. `this-formula` attr_reader :name - # The name that was used to identify this {Formula}. - # Could be the name of the {Formula}, or an alias. - # e.g. `another-name-for-this-formula` + # The path to the alias that was used to identify this {Formula}. + # e.g. `/usr/local/Library/Taps/homebrew/homebrew-core/Aliases/another-name-for-this-formula` attr_reader :alias_path + # The name of the alias that was used to identify this {Formula}. + # e.g. `another-name-for-this-formula` + attr_reader :alias_name + # The fully-qualified name of this {Formula}. # For core formula it's the same as {#name}. # e.g. `homebrew/tap-name/this-formula` attr_reader :full_name + # The fully-qualified alias referring to this {Formula}. + # For core formula it's the same as {#alias_name}. + # e.g. `homebrew/tap-name/another-name-for-this-formula` + attr_reader :full_alias_name + # The full path to this {Formula}. # e.g. `/usr/local/Library/Taps/homebrew/homebrew-core/Formula/this-formula.rb` attr_reader :path @@ -149,25 +157,31 @@ class Formula # @return [BuildOptions] attr_accessor :build + # A {Boolean} indicating whether this formula should be considered outdated + # if the target of the alias it was installed with has since changed. + # Defaults to true. + # @return [Boolean] + attr_accessor :follow_installed_alias + alias follow_installed_alias? follow_installed_alias + # @private def initialize(name, path, spec, alias_path: nil) @name = name @path = path @alias_path = alias_path + @alias_name = File.basename(alias_path) if alias_path @revision = self.class.revision || 0 @version_scheme = self.class.version_scheme || 0 - if path == Formulary.core_path(name) - @tap = CoreTap.instance - @full_name = name + @tap = if path == Formulary.core_path(name) + CoreTap.instance elsif path.to_s =~ HOMEBREW_TAP_PATH_REGEX - @tap = Tap.fetch($1, $2) - @full_name = "#{@tap}/#{name}" - else - @tap = nil - @full_name = name + Tap.fetch($1, $2) end + @full_name = get_full_name(name) + @full_alias_name = get_full_name(@alias_name) + set_spec :stable set_spec :devel set_spec :head @@ -183,6 +197,7 @@ class Formula validate_attributes! @build = active_spec.build @pin = FormulaPin.new(self) + @follow_installed_alias = true end # @private @@ -197,6 +212,16 @@ class Formula private + # Allow full name logic to be re-used between names, aliases, + # and installed aliases. + def get_full_name(name) + if name.nil? || @tap.nil? || @tap.core_tap? + name + else + "#{@tap}/#{name}" + end + end + def set_spec(name) spec = self.class.send(name) if spec.url @@ -236,11 +261,39 @@ class Formula path if path =~ %r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases} end + def installed_alias_name + File.basename(installed_alias_path) if installed_alias_path + end + + def full_installed_alias_name + get_full_name(installed_alias_name) + end + # The path that was specified to find this formula. def specified_path alias_path || path end + # The name specified to find this formula. + def specified_name + alias_name || name + end + + # The name (including tap) specified to find this formula. + def full_specified_name + full_alias_name || full_name + end + + # The name specified to install this formula. + def installed_specified_name + installed_alias_name || name + end + + # The name (including tap) specified to install this formula. + def full_installed_specified_name + full_installed_alias_name || full_name + end + # Is the currently active {SoftwareSpec} a {#stable} build? # @private def stable? @@ -1067,39 +1120,77 @@ class Formula end # @private - def outdated_versions(options = {}) - @outdated_versions ||= Hash.new do |cache, key| + def outdated_kegs(options = {}) + @outdated_kegs ||= Hash.new do |cache, key| raise Migrator::MigrationNeededError, self if migration_needed? - cache[key] = _outdated_versions(key) + cache[key] = _outdated_kegs(key) end - @outdated_versions[options] + @outdated_kegs[options] end - def _outdated_versions(options = {}) - all_versions = [] + def _outdated_kegs(options = {}) + all_kegs = [] installed_kegs.each do |keg| + all_kegs << keg version = keg.version - all_versions << version next if version.head? tab = Tab.for_keg(keg) next if version_scheme > tab.version_scheme next if version_scheme == tab.version_scheme && pkg_version > version - return [] + next if follow_installed_alias? && installed_alias_target_changed? + + return [] # this keg is the current version of the formula, so it's not outdated end + # Even if this formula hasn't been installed, there may be installations + # of other formulae which used to be targets of the alias currently + # targetting this formula. These should be counted as outdated versions. + all_kegs.concat old_installed_formulae.flat_map(&:installed_kegs) + head_version = latest_head_version if head_version && !head_version_outdated?(head_version, options) [] else - all_versions.sort + all_kegs.sort_by(&:version) end end + def current_installed_alias_target + Formulary.factory(installed_alias_path) if installed_alias_path + end + + # Has the target of the alias used to install this formula changed? + # Returns false if the formula wasn't installed with an alias. + def installed_alias_target_changed? + ![self, nil].include?(current_installed_alias_target) + end + + # Is this formula the target of an alias used to install an old formula? + def supersedes_an_installed_formula? + old_installed_formulae.any? + end + + # Has the alias used to install the formula changed, or are different + # formulae already installed with this alias? + def alias_changed? + installed_alias_target_changed? || supersedes_an_installed_formula? + end + + # If the alias has changed value, return the new formula. + # Otherwise, return self. + def latest_formula + installed_alias_target_changed? ? current_installed_alias_target : self + end + + def old_installed_formulae + alias_path ? self.class.installed_with_alias_path(alias_path) : [] + end + # @private def outdated?(options = {}) - !outdated_versions(options).empty? + !outdated_kegs(options).empty? rescue Migrator::MigrationNeededError true end @@ -1254,6 +1345,11 @@ class Formula end.compact end + def self.installed_with_alias_path(alias_path) + return [] if alias_path.nil? + installed.select { |f| f.installed_alias_path == alias_path } + end + # an array of all alias files of core {Formula} # @private def self.core_alias_files diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 1cd9dce2ae..4c20c2225b 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -77,8 +77,11 @@ class Formulary end # Gets the formula instance. - def get_formula(spec) - klass.new(name, path, spec, alias_path: alias_path) + # + # `alias_path` can be overridden here in case an alias was used to refer to + # a formula that was loaded in another way. + def get_formula(spec, alias_path: nil) + klass.new(name, path, spec, alias_path: alias_path || self.alias_path) end def klass @@ -103,7 +106,7 @@ class Formulary super name, Formulary.path(full_name) end - def get_formula(spec) + def get_formula(spec, alias_path: nil) formula = super formula.local_bottle_path = @bottle_filename formula_version = formula.pkg_version @@ -120,7 +123,7 @@ class Formulary path = alias_path.resolved_path name = path.basename(".rb").to_s super name, path - @alias_path = alias_path + @alias_path = alias_path.to_s end end @@ -175,7 +178,7 @@ class Formulary super name, path end - def get_formula(spec) + def get_formula(spec, alias_path: nil) super rescue FormulaUnavailableError => e raise TapFormulaUnavailableError.new(tap, name), "", e.backtrace @@ -187,7 +190,7 @@ class Formulary super name, Formulary.core_path(name) end - def get_formula(_spec) + def get_formula(_spec, alias_path: nil) raise FormulaUnavailableError, name end end @@ -215,38 +218,42 @@ class Formulary # * a formula pathname # * a formula URL # * a local bottle reference - def self.factory(ref, spec = :stable) - loader_for(ref).get_formula(spec) + def self.factory(ref, spec = :stable, alias_path: nil) + loader_for(ref).get_formula(spec, alias_path: alias_path) end # Return a Formula instance for the given rack. # It will auto resolve formula's spec when requested spec is nil - def self.from_rack(rack, spec = nil) + # + # The :alias_path option will be used if the formula is found not to be + # installed, and discarded if it is installed because the alias_path used + # to install the formula will be set instead. + def self.from_rack(rack, spec = nil, alias_path: nil) kegs = rack.directory? ? rack.subdirs.map { |d| Keg.new(d) } : [] keg = kegs.detect(&:linked?) || kegs.detect(&:optlinked?) || kegs.max_by(&:version) if keg - from_keg(keg, spec) + from_keg(keg, spec, :alias_path => alias_path) else - factory(rack.basename.to_s, spec || :stable) + factory(rack.basename.to_s, spec || :stable, :alias_path => alias_path) end end # Return a Formula instance for the given keg. # It will auto resolve formula's spec when requested spec is nil - def self.from_keg(keg, spec = nil) + def self.from_keg(keg, spec = nil, alias_path: nil) tab = Tab.for_keg(keg) tap = tab.tap spec ||= tab.spec f = if tap.nil? - factory(keg.rack.basename.to_s, spec) + factory(keg.rack.basename.to_s, spec, :alias_path => alias_path) else begin - factory("#{tap}/#{keg.rack.basename}", spec) + factory("#{tap}/#{keg.rack.basename}", spec, :alias_path => alias_path) rescue FormulaUnavailableError # formula may be migrated to different tap. Try to search in core and all taps. - factory(keg.rack.basename.to_s, spec) + factory(keg.rack.basename.to_s, spec, :alias_path => alias_path) end end f.build = tab @@ -346,7 +353,7 @@ class Formulary end def self.core_path(name) - CoreTap.instance.formula_dir/"#{name.downcase}.rb" + CoreTap.instance.formula_dir/"#{name.to_s.downcase}.rb" end def self.tap_paths(name, taps = Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"]) diff --git a/Library/Homebrew/test/test_formulary.rb b/Library/Homebrew/test/test_formulary.rb index 8b3417dad9..c545ff84da 100644 --- a/Library/Homebrew/test/test_formulary.rb +++ b/Library/Homebrew/test/test_formulary.rb @@ -89,7 +89,7 @@ class FormularyFactoryTest < Homebrew::TestCase FileUtils.ln_s @path, alias_path result = Formulary.factory("foo") assert_kind_of Formula, result - assert_equal alias_path, result.alias_path + assert_equal alias_path.to_s, result.alias_path ensure alias_dir.rmtree end From c874b4e4c2590cbb775583892f18ecdc96ca897c Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Thu, 15 Sep 2016 16:01:59 +0100 Subject: [PATCH 03/12] install: don't install alias if already installed --- Library/Homebrew/cmd/install.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 99cb753530..fdaf68d535 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -133,9 +133,14 @@ module Homebrew raise "No devel block is defined for #{f.full_name}" end - if f.installed? - msg = "#{f.full_name}-#{f.installed_version} already installed" - msg << ", it's just not linked" unless f.linked_keg.symlink? || f.keg_only? + current = f if f.installed? + current ||= f.old_installed_formulae.first + + if current + msg = "#{current.full_name}-#{current.installed_version} already installed" + unless current.linked_keg.symlink? || current.keg_only? + msg << ", it's just not linked" + end opoo msg elsif f.migration_needed? && !ARGV.force? # Check if the formula we try to install is the same as installed From d3788c58efbae7c841faeb89a3eea539862245f7 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Thu, 15 Sep 2016 15:53:04 +0100 Subject: [PATCH 04/12] brew outdated: use full name in verbose --- Library/Homebrew/cmd/outdated.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb index f9739cd7a0..4eb55fc50f 100644 --- a/Library/Homebrew/cmd/outdated.rb +++ b/Library/Homebrew/cmd/outdated.rb @@ -56,9 +56,11 @@ module Homebrew end outdated_versions = outdated_kegs. - group_by(&:name). - sort_by(&:first). - map { |name, kegs| "#{name} (#{kegs.map(&:version) * ", "})" } * ", " + group_by { |keg| Formulary.from_keg(keg) }. + sort_by { |formula, kegs| formula.full_name }. + map do |formula, kegs| + "#{formula.full_name} (#{kegs.map(&:version) * ", "})" + end * ", " puts "#{outdated_versions} < #{current_version}" else From 7b85934f502cd6adf4696cf63cd3cd0c4449d40f Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Thu, 15 Sep 2016 16:22:27 +0100 Subject: [PATCH 05/12] Update formula tests for #outdated_kegs --- Library/Homebrew/test/test_formula.rb | 80 +++++++++++++-------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb index 2eb64ebb67..393a0e2162 100644 --- a/Library/Homebrew/test/test_formula.rb +++ b/Library/Homebrew/test/test_formula.rb @@ -637,42 +637,42 @@ class OutdatedVersionsTests < Homebrew::TestCase tab end - def reset_outdated_versions - f.instance_variable_set(:@outdated_versions, nil) + 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_versions, :empty? + 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_versions, :empty? + 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_versions, :empty? + 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_versions, :empty? + refute_predicate f.outdated_kegs, :empty? end def test_same_head_installed f.instance_variable_set(:@tap, CoreTap.instance) setup_tab_for_prefix(head_prefix, tap: "homebrew/core") - assert_predicate f.outdated_versions, :empty? + assert_predicate f.outdated_kegs, :empty? end def test_different_head_installed f.instance_variable_set(:@tap, CoreTap.instance) setup_tab_for_prefix(head_prefix, tap: "user/repo") - assert_predicate f.outdated_versions, :empty? + assert_predicate f.outdated_kegs, :empty? end def test_mixed_taps_greater_version_installed @@ -680,12 +680,12 @@ class OutdatedVersionsTests < Homebrew::TestCase setup_tab_for_prefix(outdated_prefix, tap: "homebrew/core") setup_tab_for_prefix(greater_prefix, tap: "user/repo") - assert_predicate f.outdated_versions, :empty? + assert_predicate f.outdated_kegs, :empty? setup_tab_for_prefix(greater_prefix, tap: "homebrew/core") - reset_outdated_versions + reset_outdated_kegs - assert_predicate f.outdated_versions, :empty? + assert_predicate f.outdated_kegs, :empty? end def test_mixed_taps_outdated_version_installed @@ -695,38 +695,38 @@ class OutdatedVersionsTests < Homebrew::TestCase setup_tab_for_prefix(outdated_prefix) setup_tab_for_prefix(extra_outdated_prefix, tap: "homebrew/core") - reset_outdated_versions + reset_outdated_kegs - refute_predicate f.outdated_versions, :empty? + refute_predicate f.outdated_kegs, :empty? setup_tab_for_prefix(outdated_prefix, tap: "user/repo") - reset_outdated_versions + reset_outdated_kegs - refute_predicate f.outdated_versions, :empty? + refute_predicate f.outdated_kegs, :empty? end def test_same_version_tap_installed f.instance_variable_set(:@tap, CoreTap.instance) setup_tab_for_prefix(same_prefix, tap: "homebrew/core") - assert_predicate f.outdated_versions, :empty? + assert_predicate f.outdated_kegs, :empty? setup_tab_for_prefix(same_prefix, tap: "user/repo") - reset_outdated_versions + reset_outdated_kegs - assert_predicate f.outdated_versions, :empty? + 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_versions, :empty? + 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_versions + reset_outdated_kegs - assert_predicate f.outdated_versions, :empty? + assert_predicate f.outdated_kegs, :empty? end def test_outdated_fetch_head @@ -764,20 +764,20 @@ class OutdatedVersionsTests < Homebrew::TestCase end end - refute_predicate f.outdated_versions(fetch_head: true), :empty? + refute_predicate f.outdated_kegs(fetch_head: true), :empty? tab_a.source["versions"] = { "stable" => f.version.to_s } tab_a.write - reset_outdated_versions - refute_predicate f.outdated_versions(fetch_head: true), :empty? + reset_outdated_kegs + refute_predicate f.outdated_kegs(fetch_head: true), :empty? head_prefix_a.rmtree - reset_outdated_versions - refute_predicate f.outdated_versions(fetch_head: true), :empty? + 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_versions - assert_predicate f.outdated_versions(fetch_head: true), :empty? + reset_outdated_kegs + assert_predicate f.outdated_kegs(fetch_head: true), :empty? ensure ENV.replace(initial_env) testball_repo.rmtree if testball_repo.exist? @@ -788,7 +788,7 @@ class OutdatedVersionsTests < Homebrew::TestCase FileUtils.rm_rf HOMEBREW_CELLAR/"testball" end - def test_outdated_versions_version_scheme_changed + def test_outdated_kegs_version_scheme_changed @f = formula("testball") do url "foo" version "20141010" @@ -798,12 +798,12 @@ class OutdatedVersionsTests < Homebrew::TestCase prefix = HOMEBREW_CELLAR.join("testball/0.1") setup_tab_for_prefix(prefix, versions: { "stable" => "0.1" }) - refute_predicate f.outdated_versions, :empty? + refute_predicate f.outdated_kegs, :empty? ensure prefix.rmtree end - def test_outdated_versions_mixed_version_schemes + def test_outdated_kegs_mixed_version_schemes @f = formula("testball") do url "foo" version "20141010" @@ -816,23 +816,23 @@ class OutdatedVersionsTests < Homebrew::TestCase 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_versions, :empty? - reset_outdated_versions + 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_versions, :empty? - reset_outdated_versions + 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_versions, :empty? + assert_predicate f.outdated_kegs, :empty? ensure f.rack.rmtree end - def test_outdated_versions_head_with_version_scheme + def test_outdated_kegs_head_with_version_scheme @f = formula("testball") do url "foo" version "1.0" @@ -842,13 +842,13 @@ class OutdatedVersionsTests < Homebrew::TestCase head_prefix = HOMEBREW_CELLAR.join("testball/HEAD") setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0", "version_scheme" => 1 }) - refute_predicate f.outdated_versions, :empty? + refute_predicate f.outdated_kegs, :empty? - reset_outdated_versions + reset_outdated_kegs head_prefix.rmtree setup_tab_for_prefix(head_prefix, versions: { "stable" => "1.0", "version_scheme" => 2 }) - assert_predicate f.outdated_versions, :empty? + assert_predicate f.outdated_kegs, :empty? ensure head_prefix.rmtree end From 8bae49941c5fc3894efad334c9780d10c2572592 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 17 Sep 2016 17:05:51 +0100 Subject: [PATCH 06/12] outdated: prefer .join over *. --- Library/Homebrew/cmd/outdated.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb index 4eb55fc50f..dfb64a282a 100644 --- a/Library/Homebrew/cmd/outdated.rb +++ b/Library/Homebrew/cmd/outdated.rb @@ -59,8 +59,8 @@ module Homebrew group_by { |keg| Formulary.from_keg(keg) }. sort_by { |formula, kegs| formula.full_name }. map do |formula, kegs| - "#{formula.full_name} (#{kegs.map(&:version) * ", "})" - end * ", " + "#{formula.full_name} (#{kegs.map(&:version).join(", ")})" + end.join(", ") puts "#{outdated_versions} < #{current_version}" else From 49bfb3fb4a29c28e2a32e2421dae680dafcf121d Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Sun, 18 Sep 2016 19:43:54 +0100 Subject: [PATCH 07/12] Make Formula#installed_alias_target_changed? clearer --- Library/Homebrew/formula.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 41ddda3076..5368941f74 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1164,7 +1164,8 @@ class Formula # Has the target of the alias used to install this formula changed? # Returns false if the formula wasn't installed with an alias. def installed_alias_target_changed? - ![self, nil].include?(current_installed_alias_target) + target = current_installed_alias_target + target && target != self end # Is this formula the target of an alias used to install an old formula? From 912b8d47767d4f963cae9e9698e29590d9d9d69b Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Sun, 18 Sep 2016 21:43:46 +0100 Subject: [PATCH 08/12] formula tests: more consistent names --- Library/Homebrew/test/test_formula.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb index 393a0e2162..15506a8e2e 100644 --- a/Library/Homebrew/test/test_formula.rb +++ b/Library/Homebrew/test/test_formula.rb @@ -663,19 +663,19 @@ class OutdatedVersionsTests < Homebrew::TestCase refute_predicate f.outdated_kegs, :empty? end - def test_same_head_installed + 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_different_head_installed + 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_mixed_taps_greater_version_installed + 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") @@ -688,7 +688,7 @@ class OutdatedVersionsTests < Homebrew::TestCase assert_predicate f.outdated_kegs, :empty? end - def test_mixed_taps_outdated_version_installed + def test_outdated_mixed_taps_outdated_version_installed f.instance_variable_set(:@tap, CoreTap.instance) extra_outdated_prefix = HOMEBREW_CELLAR/"#{f.name}/1.0" @@ -705,7 +705,7 @@ class OutdatedVersionsTests < Homebrew::TestCase refute_predicate f.outdated_kegs, :empty? end - def test_same_version_tap_installed + def test_outdated_same_version_tap_installed f.instance_variable_set(:@tap, CoreTap.instance) setup_tab_for_prefix(same_prefix, tap: "homebrew/core") From e12c23faa87aa9c6f493dcd4aa47d1124c41a418 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Sun, 18 Sep 2016 23:59:55 +0100 Subject: [PATCH 09/12] Formula#old_installed_formula?: fix some bugs Previously, this method would: - Include the current formula - Include the current target of the installed alias, which by definition is not "old" This commit fixes both of these issues. Only formulae that are the current target of the alias they were installed with are now considered to have old installed formulae. --- Library/Homebrew/formula.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 5368941f74..795646d08e 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1186,7 +1186,11 @@ class Formula end def old_installed_formulae - alias_path ? self.class.installed_with_alias_path(alias_path) : [] + # If this formula isn't the current target of the alias, + # it doesn't make sense to say that other formulae are older versions of it + # because we don't know which came first. + return [] if alias_path.nil? || installed_alias_target_changed? + self.class.installed_with_alias_path(alias_path) - [self] end # @private From 4abd48812bb544426442749266acb13a20ce434c Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Mon, 19 Sep 2016 00:20:43 +0100 Subject: [PATCH 10/12] alias tracking: add formula tests --- Library/Homebrew/test/test_formula.rb | 303 +++++++++++++++++++++++++- Library/Homebrew/test/testing_env.rb | 8 + 2 files changed, 307 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb index 15506a8e2e..3a021b4f6d 100644 --- a/Library/Homebrew/test/test_formula.rb +++ b/Library/Homebrew/test/test_formula.rb @@ -8,15 +8,154 @@ class FormulaTests < Homebrew::TestCase name = "formula_name" path = Formulary.core_path(name) spec = :stable - alias_path = CoreTap.instance.alias_dir/"formula_alias" + + 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" + full_name = "#{tap.user}/#{tap.repo}/#{name}" + 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 @@ -274,6 +413,36 @@ class FormulaTests < Homebrew::TestCase 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" @@ -607,29 +776,111 @@ class FormulaTests < Homebrew::TestCase end end +class AliasChangeTests < Homebrew::TestCase + attr_reader :f, :new_formula, :tab, :alias_path + + def make_formula(version) + f = formula(alias_path: alias_path) { url "foo-#{version}" } + f.build = tab + f + end + + def setup + alias_name = "bar" + @alias_path = "#{CoreTap.instance.alias_dir}/#{alias_name}" + + @tab = Tab.empty + + @f = make_formula("1.0") + @new_formula = make_formula("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 - attr_reader :f + attr_reader :outdated_prefix, + :same_prefix, + :greater_prefix, + :head_prefix, + :old_alias_target_prefix + attr_reader :f, :old_formula def setup @f = formula do url "foo" version "1.20" end + + @old_formula = formula("foo@1") { url "foo-1.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 teardown - @f.rack.rmtree if @f.rack.exist? + [@f.rack, @old_formula.rack].select(&:exist?).each(&:rmtree) + 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 @@ -663,6 +914,50 @@ class OutdatedVersionsTests < Homebrew::TestCase 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 + f.follow_installed_alias = true + f.build = setup_tab_for_prefix(same_prefix, path: alias_path) + stub_formula_loader(formula("foo@2") { url "foo-2.0" }, alias_path) + refute_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") diff --git a/Library/Homebrew/test/testing_env.rb b/Library/Homebrew/test/testing_env.rb index cbbc5eff12..aa89ba2bd4 100644 --- a/Library/Homebrew/test/testing_env.rb +++ b/Library/Homebrew/test/testing_env.rb @@ -121,5 +121,13 @@ module Homebrew ENV.replace old end end + + # Use a stubbed {Formulary::FormulaLoader} to make a given formula be found + # when loading from {Formulary} with `ref`. + def stub_formula_loader(formula, ref = formula.name) + loader = mock + loader.stubs(:get_formula).returns(formula) + Formulary.stubs(:loader_for).with(ref).returns(loader) + end end end From 6ec14288aa727eeb3d3a7773720695394697a62a Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Mon, 19 Sep 2016 01:53:48 +0100 Subject: [PATCH 11/12] Formulae aren't outdated if replacement formula installed --- Library/Homebrew/formula.rb | 8 +++++++- Library/Homebrew/test/test_formula.rb | 18 ++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 795646d08e..238c6c4bcf 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1139,7 +1139,9 @@ class Formula tab = Tab.for_keg(keg) next if version_scheme > tab.version_scheme next if version_scheme == tab.version_scheme && pkg_version > version - next if follow_installed_alias? && installed_alias_target_changed? + + # don't consider this keg current if there's a newer formula available + next if follow_installed_alias? && new_formula_available? return [] # this keg is the current version of the formula, so it's not outdated end @@ -1157,6 +1159,10 @@ class Formula end end + def new_formula_available? + installed_alias_target_changed? && !latest_formula.installed? + end + def current_installed_alias_target Formulary.factory(installed_alias_path) if installed_alias_path end diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb index 3a021b4f6d..9f39dd2d09 100644 --- a/Library/Homebrew/test/test_formula.rb +++ b/Library/Homebrew/test/test_formula.rb @@ -851,7 +851,7 @@ class OutdatedVersionsTests < Homebrew::TestCase :greater_prefix, :head_prefix, :old_alias_target_prefix - attr_reader :f, :old_formula + attr_reader :f, :old_formula, :new_formula def setup @f = formula do @@ -860,6 +860,7 @@ class OutdatedVersionsTests < Homebrew::TestCase 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" @@ -869,7 +870,8 @@ class OutdatedVersionsTests < Homebrew::TestCase end def teardown - [@f.rack, @old_formula.rack].select(&:exist?).each(&:rmtree) + formulae = [@f, @old_formula, @new_formula] + formulae.map(&:rack).select(&:exist?).each(&:rmtree) end def alias_path @@ -921,13 +923,21 @@ class OutdatedVersionsTests < Homebrew::TestCase assert_predicate f.outdated_kegs, :empty? end - def test_outdated_follow_alias_and_alias_changed + 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(formula("foo@2") { url "foo-2.0" }, 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) From d89a696d83cd325656e30a99cb4ba8342ffb39c9 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Mon, 19 Sep 2016 02:24:21 +0100 Subject: [PATCH 12/12] Remove unused variable This will make brew readall --syntax pass --- Library/Homebrew/test/test_formula.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb index 9f39dd2d09..1da60c2143 100644 --- a/Library/Homebrew/test/test_formula.rb +++ b/Library/Homebrew/test/test_formula.rb @@ -126,7 +126,6 @@ class FormulaTests < Homebrew::TestCase def test_installed_alias_with_tap tap = Tap.new("user", "repo") name = "foo" - full_name = "#{tap.user}/#{tap.repo}/#{name}" path = "#{tap.path}/Formula/#{name}.rb" f = formula(name, path) { url "foo-1.0" }